package Analizador_Sintactico;


import Analizador_Lexico.*;
import Analizador_Lexico.Token.TipoToken;
import generator.*;

import exceptions.lexic.*;
import exceptions.sintactic.*;
import exceptions.semantic.*;

import java.io.*;
import java.util.*;
import java.io.IOException;
import Tabla_Simbolos.*;

public class AnalizadorSintactico {

	private AnalizadorLexico al;
    private static CodeGenerator cGen;
    private LabelGenerator lGen;
	private Token token;
    boolean esVar = false;
    int aux = 1;
    private String typeSentence = "";
    
    //variables utilizadas en el chequeo semantico
    private TSGeneral tsG;
    private TClase tsO;
    private TClase tsC;
    TClase claseActual; // variable global de tipo TClase que actualizamos con la clase actual que estamos analizando  en el momento.
    boolean definicion = false; // variable global usada para indicar si estamos chequeando la declaración o la definición 
	int posArg = 0; // variable global utilizada como contador al momento de recorrer los argumentos de un metodo
	Metodo metActual;
	private boolean ladoIzq = true;
	
	//LISTA DE INSTRUCCIONES QUE TENGO QUE GENERAR AL FINAL (PORQUE NO SE LOS OFFSET DE LAS VARIABLES HASTA ESE MOMENTO)
	private LinkedList<InstructionGenerate> listInstruction = new LinkedList<InstructionGenerate>();
	//LISTA DE INSTRUCCIONES QUE TENGO QUE GENERAR AL FINAL (PORQUE NO SE LOS OFFSET DE LAS VARIABLES HASTA ESE MOMENTO)
	private LinkedList<InstructionClassGenerate> listClassInstruction = new LinkedList<InstructionClassGenerate>();
    
    //CONSTRUCTOR
    public AnalizadorSintactico(String input,CodeGenerator CG) throws IOException{
    	inicializar();//inicializa datos para el semantico
        al = new AnalizadorLexico(input);
        cGen = CG;
		lGen = new LabelGenerator();
    }
    
    //	METODOS UTILIZADOS PARA LOS DISTINTOS CHEQUEOS SEMANTICOS
    //************************************************************
    //metodo que se llama en el constructor del analizador sintactico y 
    //sirve para inicializar objetos y variables para el chequeo semantico
    private void inicializar(){
		tsG= new TSGeneral();
		tsO=new TClase("Object",true);
		tsO.setPadre(tsO);
		tsG.agregarClase(tsO);
		tsC=new TClase("String",true);
		tsC.setPadre(tsO);
		tsG.agregarClase(tsC);
	}
    
    //crea la clase System
    private void semantico_start(){
        TClase Sym = new TClase("System",true);
        Sym.setPadre(tsO);
		tsG.agregarClase(Sym);
		
		Metodo system = new Metodo("System",new TVoid());
		Sym.agregarMetodo(system);
		
		Metodo read = new Metodo("read",new Entero());
		Sym.agregarMetodo(read);
		
		Metodo printB = new Metodo("printB",new TVoid());
		printB.agregarAtributo(new Atributo("",new Booleano()));
		Sym.agregarMetodo(printB);
		
		Metodo printC = new Metodo("printC",new TVoid());
		printC.agregarAtributo(new Atributo("",new Caracter()));
		Sym.agregarMetodo(printC);
		
		Metodo printI = new Metodo("printI",new TVoid());
		printI.agregarAtributo(new Atributo("",new Entero()));
		Sym.agregarMetodo(printI);
		
		Metodo printS = new Metodo("printS",new TVoid());
		printS.agregarAtributo(new Atributo("",new Cadena()));
		Sym.agregarMetodo(printS);
		
		Metodo println = new Metodo("println",new TVoid());
		Sym.agregarMetodo(println);
		
		Metodo printBln = new Metodo("printBln",new TVoid());
		printBln.agregarAtributo(new Atributo("",new Booleano()));
		Sym.agregarMetodo(printBln);
		
		Metodo printCln = new Metodo("printCln",new TVoid());
		printCln.agregarAtributo(new Atributo("",new Caracter()));
		Sym.agregarMetodo(printCln);
		
		Metodo printIln = new Metodo("printIln",new TVoid());
		printIln.agregarAtributo(new Atributo("",new Entero()));
		Sym.agregarMetodo(printIln);
		
		Metodo printSln = new Metodo("printSln",new TVoid());
		printSln.agregarAtributo(new Atributo("",new Cadena()));
		Sym.agregarMetodo(printSln);
    }
    
	//FIN METODOS PARA SEMANTICO
	public Token getToken(){
        return token;
    }

    public int getLinea(){
        return token.getLinea();
    }
    
    public int getColumna(){
        return token.getColumna();
    }
    
    public static CodeGenerator getCodeGen(){
        return cGen;
    }
        
    //Recupera el próximo token del archivo fuente
    private void consumirToken() throws LexicException, SintacticException{
		try{
            token = al.getToken();
            //System.out.print(token.getLexema());
        }catch(IOException e){
        	cGen.close();
        	al.getBuffer().cerrarArchivo();
            throw new UnexpectedEOFException();
        }
	}
    
    //Recupera el próximo token del archivo fuente con la condición que:
    //el tipo del token actual, sea igual al tipo esperado (el que recibe como parametro)
    private void consumirTokenCondicional(Token.TipoToken t) throws LexicException, SintacticException{          	
        if (token.getTipo()!=t){        	
        //si no es el tipo de token esperado, se arroja una exception detallando
        	cGen.close();
        	al.getBuffer().cerrarArchivo();
        	if (typeSentence.equals("")){
            	throw (new UnexpectedTokenException(t, token.getTipo(), token.getLinea(),token.getColumna()));
        	}else {
        		throw (new StatementException(typeSentence,t, token.getTipo(), token.getLinea(),token.getColumna()));
        	}
        }
        else {
            consumirToken();
        }
	}
    
    //hace una comparacion entre el tipo del token actual y del enviado como parametro
    private boolean equalType(Token.TipoToken t){
        return (token.getTipo() ==t);
    }
    
    //método inicial donde se comienza a analizar el archivo fuente (lexica y sintacticamente)
    public void parsear() throws SintacticException, LexicException, IOException,SemanticException{
        consumirToken();
		Start();
		cGen.close();
		System.out.println();
		System.out.println("COMPILACION EXITOSA!");
        try{
        // columna le asigno 0 por defecto porque no tengo la columna y si es fin de archivo no es necesario devolverla en la excepcion
		if (al.getToken().getTipo()!=Token.getTipo("EOF",token.getLinea(),0)) 
			throw new EOFExpectedException(token.getLinea(),token.getColumna());
        }catch(IOException e){
        	cGen.close();
        	al.getBuffer().cerrarArchivo();
            throw new EOFExpectedException(token.getLinea(),token.getColumna());
        }
    }


/************************IMPLEMENTACION DE LA GRAMATICA*****************************************/
    
   /*   CLASSDEF:    */       
    private void Start() throws LexicException, SintacticException, SemanticException{
    	//*************************************************
    	//GENERACIÓN CI
    	//CODIGO DE INICIALIZACION	
		cGen.generate(".CODE");
		cGen.generate("PUSH heap_init");
		cGen.generate("CALL");
		//genero codigo para llamar al main
		cGen.generate("PUSH Main					;label de main"); 
		cGen.generate("CALL");
		cGen.generate("HALT ");
		cGen.generate("malloc: LOADFP 					;Inicialización unidad	");
		cGen.generate("LOADSP ");
		cGen.generate("STOREFP					;Finaliza inicialización del RA");
		cGen.generate("LOADHL 					;hl");
		cGen.generate("DUP 						;hl");
		cGen.generate("PUSH 1 					;1");
		cGen.generate("ADD 						;hl+1");
		cGen.generate("STORE 4 					;Guarda el resultado (un puntero a la primer celda de la región de memoria)");
		cGen.generate("LOAD 3 					;Carga la cantidad de celdas a alojar (parámetro que debe ser positivo)");
		cGen.generate("ADD ");
		cGen.generate("STOREHL 					;Mueve el heap limit (hl). Expande el heap");
		cGen.generate("STOREFP ");
		cGen.generate("RET 1 					;Retorna eliminando el parámetro");
		cGen.generate("heap_init: RET 0 					;Retorna inmediatamente");
		//Generar la VT de System y todos sus métodos.
		cGen.generate(".DATA");
		cGen.generate("VT_System: DW lSystemSystem, lreadSystem,lprintBSystem, lprintCSystem, lprintISystem, lprintSSystem, lprintlnSystem, lprintBlnSystem, lprintClnSystem, lprintIlnSystem, lprintSlnSystem");
		
		cGen.generate(".CODE");
		
		cGen.generate(";ACA EMPIEZA EL METODO System");
		cGen.generate("lSystemSystem: NOP");
		cGen.generate("LOADFP"); 
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 1");
		
		cGen.generate(";ACA EMPIEZA EL METODO read");
		cGen.generate("lreadSystem: NOP");
		cGen.generate("LOADFP"); 
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("READ"); 
		cGen.generate("PUSH 48");
		cGen.generate("SUB"); 
		cGen.generate("STORE 4");
		cGen.generate("STOREFP"); 
		cGen.generate("RET 1");
		
		cGen.generate(";ACA EMPIEZA EL METODO printB");
		cGen.generate("lprintBSystem: NOP");
		cGen.generate("LOADFP"); 
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("BPRINT"); 
	    cGen.generate("STOREFP"); 
		cGen.generate("RET 2");
				
		cGen.generate(";ACA EMPIEZA EL METODO printC");
		cGen.generate("lprintCSystem: NOP");
		cGen.generate("LOADFP");
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("CPRINT"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 2");
		
		cGen.generate(";ACA EMPIEZA EL METODO printI");
		cGen.generate("lprintISystem: NOP");
		cGen.generate("LOADFP");
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("IPRINT"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 2");
		
		cGen.generate(";ACA EMPIEZA EL METODO printS");
		cGen.generate("lprintSSystem: NOP");
		cGen.generate("LOADFP");
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("SPRINT"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 2");

		cGen.generate(";ACA EMPIEZA EL METODO println");
		cGen.generate("lprintlnSystem: NOP");
		cGen.generate("LOADFP"); 
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("PRNLN"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 1");
		
		cGen.generate(";ACA EMPIEZA EL METODO printBln");
		cGen.generate("lprintBlnSystem: NOP");
		cGen.generate("LOADFP"); 
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("BPRINT"); 
		cGen.generate("PRNLN"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 2");
		
		cGen.generate(";ACA EMPIEZA EL METODO printCln");
		cGen.generate("lprintClnSystem: NOP");
		cGen.generate("LOADFP"); 
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("CPRINT"); 
		cGen.generate("PRNLN"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 2");
		
		cGen.generate(";ACA EMPIEZA EL METODO printIln");
		cGen.generate("lprintIlnSystem: NOP");
		cGen.generate("LOADFP");
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("IPRINT"); 
		cGen.generate("PRNLN"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 2");
		
		cGen.generate(";ACA EMPIEZA EL METODO printSln");
		cGen.generate("lprintSlnSystem: NOP");
		cGen.generate("LOADFP");
		cGen.generate("LOADSP"); 
		cGen.generate("STOREFP"); 
		cGen.generate("LOAD 4");
		cGen.generate("SPRINT"); 
		cGen.generate("PRNLN"); 
		cGen.generate("STOREFP"); 
		cGen.generate("RET 2");
		
		//*************************************************
		semantico_start(); //chequeo semantico
    	if (equalType(Token.TipoToken.CLASSDEF)){
    		while (equalType(Token.TipoToken.CLASSDEF)){
    			ClassDef();    			
    		}
    		//CONTROLO QUE TODAS LAS CLASES NOMBRADAS EN EL CLASS DEF ESTEN DECLARADAS
    		Enumeration<String> eClase = tsG.getTablaClases().keys();  
            String nameClase;  
            TClase claseAux;
            while (eClase.hasMoreElements()) {  
               nameClase = eClase.nextElement(); 
               claseAux = tsG.getTablaClases().get(nameClase);
               if (claseAux.getEnClassDef()!=true){
            	   throw new ClassNoFoundException(token.getLinea(),claseAux.getNombre());
               }else{
            	   if (!(claseAux.getPadre().getNombre().equals("Object"))){
            		   //CONTROLO LOS METODOS REDEFINIDOS
            		   chequearMetodos(claseAux);
            	   }
               }
             //SETEO LOS OFFSET TE LOS METODOS!!
               claseAux.setOffsetsMetodos();
            } 
    		while (equalType(Token.TipoToken.CLASS)){
    			Class();
    		}
    		//controlo que luego de los classDef y de los class la cantidad de clases sea 0, es decir, se declaro la misma cantidad que las que se definio
    		if (tsG.getCantClases()!=0)
    			throw new IndefiniteClassException(token.getLinea());
    		
    		//CALCULO DE LOS CIR LOCALES DE TODAS LAS CLASES
    		//Se resuelven todos los tamaños de CIRs pendientes (a nivel local)
    		Hashtable <String, TClase> tableClases = tsG.getTablaClases();
			Set<String> clases = tableClases.keySet();
			for (String clase : clases){
				TClase objClase = tableClases.get(clase);
				objClase.setTamañoCIRLocal();
			}
			//CALCULO DE LOS CIR TOTAL DE TODAS LAS CLASES
    		//Se resuelven todos los tamaños de CIRs pendientes (con var heredadas)
    		Hashtable <String, TClase> tableClasesTotal = tsG.getTablaClases();
			Set<String> clasesTotal = tableClasesTotal.keySet();
			for (String clase : clasesTotal){
				TClase objClase = tableClasesTotal.get(clase);
				objClase.setTamañoCIR();
			}
    		// AHORA TENGO QUE SETEAR TODOS LOS OFFSET DE LAS VARIABLES!!!
    		//Se resuelven todos los offsets pendientes en el acceso a variables de instancia de los CIR
			for (String clase : clases){
				TClase objClase = tableClases.get(clase);
				Hashtable <String, Variable> tableVar = objClase.getTablaVariable();
				Set<String> variables = tableVar.keySet();
				for (String var : variables){
					Variable objVar = tableVar.get(var);
					objVar.setOffset(objClase);
				}
			}	
		  //*******************************************************************************************
			//GENERACIÓN CI
			cGen.generate("JUMP FINAL");
			//OFFSETS DE VARIABLES DE INSTANCIA 
			int ind = 0;
			InstructionGenerate IG;
			while ((ind < listInstruction.size())){
				IG = listInstruction.get(ind);
				String instruction = IG.getInstruccion();
				String label = IG.getLabel();
				String labelRet = IG.getLabelRetorno();
				Variable var = IG.getVariable();
				cGen.generate(label+": "+instruction+" "+var.getOffset()+"					;offset en CIR de la var "+var.getNombre());  
				cGen.generate("JUMP "+labelRet);
				ind++;
			}
			
			//TAMAÑO DE CIR DE LAS CLASES 
			ind = 0;
			InstructionClassGenerate ICG;
			while ((ind < listClassInstruction.size())){
				ICG = listClassInstruction.get(ind);
				String instruction = ICG.getInstruccion();
				String label = ICG.getLabel();
				String labelRet = ICG.getLabelRetorno();
				TClase cl = ICG.getClase();
				cGen.generate(label+": "+instruction+" "+cl.getTamañoCIR()+"					;tamanio del CIR de la clase "+cl.getNombre());  
				cGen.generate("JUMP "+labelRet);
				ind++;
			}
			cGen.generate("FINAL: NOP");

		  //*******************************************************************************************
    	}else{
			// no encontro un classDef, error en declaracion del classDef
    		cGen.close();
			al.getBuffer().cerrarArchivo();
			throw new classDefDeclarationException(token.getLexema(), token.getLinea(),token.getColumna());
		}
 	}

	private void ClassDef() throws LexicException, SintacticException, SemanticException{
		consumirTokenCondicional(Token.TipoToken.CLASSDEF);
		String identifier = token.getLexema();
		consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
		
		//chequeo semantico
		if (tsG.existeClase(identifier)){
			if (tsG.getClase(identifier).getEnClassDef()==true){
				throw new RepeatedClassException(token.getLinea(),identifier);
			}else{
				tsG.getClase(identifier).setEnClassDef(true);
				tsG.aumentarCantClases();
				claseActual = tsG.getClase(identifier);
				if (identifier.equals("Main")){ // si la clase es main seteo la variable booleana claseMain en true
					tsG.setMain(true);
				}
			}
		}else{
			claseActual = new TClase(identifier,true);
			tsG.aumentarCantClases();
			tsG.agregarClase(claseActual);
			if (identifier.equals("Main")){ // si la clase es main seteo la variable booleana claseMain en true
				tsG.setMain(true);
			}
		}
		//fin chequeo
		Super();
		consumirTokenCondicional(Token.TipoToken.LLAVEABRE);
		CosasDef();
		consumirTokenCondicional(Token.TipoToken.LLAVECIERRA);
	}
		
	private void CosasDef() throws LexicException, SintacticException, SemanticException{
		if (equalType(Token.TipoToken.IDENTIFICADOR)){
			String identifier = token.getLexema();
			consumirToken();
			CtorOMethodDef(identifier);// <CtorOMethodDef>.claseOCtor = identifier.getLexema();
		}else{
			if (equalType(Token.TipoToken.VOID)){
				consumirToken();
				String identifier = token.getLexema();
				consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
				// CHEQUEO SEMANTICO BEGIN		
				if (claseActual.existeMetodo(identifier)){	
					throw new RepeatedMethodException(token.getLinea(),identifier);
				}else{
					if (claseActual.getPadre().getEnClassDef()==true){
						if (claseActual.existeMetodoEnPadre(identifier)){
							Metodo MetPadre = claseActual.getMetodo(identifier);
							TVoid tipo = new TVoid();
							if (MetPadre.mismoTipo(tipo)==false){
								throw new RedefinitionMethodException(token.getLinea(),identifier,claseActual.getNombre());
							}
						}else{
							if ((claseActual.getNombre().equals(identifier)) && (!((identifier.equals("Main"))||(identifier.equals("main"))))){
								throw new MethodClassNameException(token.getLinea(), identifier);
							}
						}
					}
				}
				// heredo el nuevo metodo pero no lo agrego a la clase actual hasta no asegurarme que no hay error en los argumentos
				Metodo Met = new Metodo(identifier,new TVoid());
				// CHEQUEO SEMANTICO END
				RestoMethodDef(Met);  //<RestoMethodDef>.metodo = Met;
			}else{
				if (!(equalType(Token.TipoToken.LLAVECIERRA))){
					Tipo tipo = PrimitiveType();
					String identifier = token.getLexema();
					consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
					// CHEQUEO SEMANTICO BEGIN
					if (claseActual.existeMetodo(identifier)){
						throw new RepeatedMethodException(token.getLinea(),identifier);
					}else{
						if (claseActual.getPadre().getEnClassDef()==true){
							if (claseActual.existeMetodoEnPadre(identifier)){
								Metodo MetPadre = claseActual.getMetodo(identifier);
								if (MetPadre.mismoTipo(tipo)==false){
									throw new RedefinitionMethodException(token.getLinea(),identifier,claseActual.getNombre());
								}
							}else{
								if (claseActual.getNombre().equals(identifier)){
									throw new MethodClassNameException(token.getLinea(),identifier);
								}
							}
						}
					}
					// heredo el nuevo metodo pero no lo agrego a la clase actual hasta no asegurarme que no hay error en los argumentos
					Metodo Met = new Metodo(identifier,tipo);
				  
					// CHEQUEO SEMANTICO END
					RestoMethodDef(Met);   // <RestoMethodDef>.metodo = Met;
				}
			}
		}
	}

	private void CtorOMethodDef(String claseOCtor) throws LexicException, SintacticException, SemanticException{
		if (equalType(Token.TipoToken.IDENTIFICADOR)){
			String identifier = token.getLexema();
			consumirToken();
			// es un metodo con tipo de retorno TClase
			// CHEQUEO SEMANTICO BEGIN
			if (!(tsG.existeClase(claseOCtor))){
				TClase claseNueva = new TClase(claseOCtor,false);
				tsG.agregarClase(claseNueva);
			}
			if (claseActual.existeMetodo(identifier)){
				throw new RepeatedMethodException(token.getLinea(),identifier);
			}else{
				if (claseActual.getPadre().getEnClassDef()==true){
					if (claseActual.existeMetodoEnPadre(identifier)){
						Metodo MetPadre = claseActual.getMetodo(identifier);
						TClase tipo;
						if (tsG.existeClase(claseOCtor)){
							tipo = tsG.getClase(claseOCtor);
						}else{
							tipo = new TClase(claseOCtor,false);
							tsG.agregarClase(tipo);
						}
						if (MetPadre.mismoTipo(tipo)==false){
							throw new RedefinitionMethodException(token.getLinea(),identifier,claseActual.getNombre());
						}
					}else{
						if (claseActual.getNombre().equals(identifier)){
							throw new MethodClassNameException(token.getLinea(),identifier);
						}
					}
				}
			}	
			
			// heredo el nuevo metodo pero no lo agrego a la clase actual hasta no asegurarme que no hay error en los argumentos
			TClase tipo;
			if (tsG.existeClase(claseOCtor)){
				tipo = tsG.getClase(claseOCtor);
			}else{
				tipo = new TClase(claseOCtor,false);
				tsG.agregarClase(tipo);
			}
			Metodo Met = new Metodo(identifier,tipo);
			
			// CHEQUEO SEMANTICO END
			RestoMethodDef(Met);  //<RestoMethodDef>.metodo = Met;
		}else{
			// esto es un constructor!!
			// CHEQUEO SEMANTICO BEGIN
			if (!(claseActual.getNombre().equals(claseOCtor))){
				throw new InvalidConstructorNameException(token.getLinea(),claseOCtor);
			}else{
				if (claseActual.existeMetodo(claseOCtor))
					throw new DoubleConstructorException(token.getLinea(),claseOCtor);
				else{
					Metodo Met = new Metodo(claseOCtor, new TVoid());
					FormalArgs(Met); 
				}
			}
			//CHEQUEO SEMANTICO END
			
			consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
			CosasDef();
		}
	}

	private void RestoMethodDef(Metodo metodo) throws LexicException, SintacticException, SemanticException{
		//SEMANTICO BEGIN
		// si no tiene constructor, el compilador le asignará un constructor sin argumentos(con código vacio)
		if (!(claseActual.getNombre().equals("Main"))){
			if (!(claseActual.existeMetodo(claseActual.getNombre()))){
				Metodo Met = new Metodo(claseActual.getNombre(),new TVoid());
				claseActual.agregarMetodo(Met);
				claseActual.decrementarCantMetodos();
			  //*************************************************
		    	//GENERACIÓN CI
				cGen.generate(";ACA EMPIEZA EL CONTRUCTOR POR DEFECTO DE "+claseActual.getNombre());
				cGen.generate("l"+claseActual.getNombre()+claseActual.getNombre()+ ": LOADFP");
				cGen.generate("LOADSP");
				cGen.generate("STOREFP");
				cGen.generate("STOREFP");
				cGen.generate("RET 1");
			  //*************************************************
			}
			// heredo el metodo al cual le tengo que agregar los argumentos formales
		}
		//SEMANTICO END	
		FormalArgs(metodo); //<FormalArgs>.metodo = <RestoMethodDef>.metodo;
		consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
		MethodsDef();		
	}

	private void MethodsDef() throws LexicException, SintacticException, SemanticException{
		if (!(equalType(Token.TipoToken.LLAVECIERRA))){
			MethodDef();
			MethodsDef();
		}
	}

	private void MethodDef() throws LexicException, SintacticException, SemanticException{
		Metodo Met;
		if (equalType(Token.TipoToken.VOID)){
			consumirToken();
			//SEMANTICO BEGIN 
			String identifier = token.getLexema();
			if (claseActual.existeMetodo(identifier)){
				throw new RepeatedMethodException(token.getLinea(),identifier);
			}else {
				if ((claseActual.getNombre().equals(identifier)) && (!((identifier.equals("Main"))||(identifier.equals("main"))))){
					throw new MethodClassNameException(token.getLinea(), identifier);
				}else{
					if (claseActual.getPadre().getEnClassDef()==true){
						if (claseActual.existeMetodoEnPadre(identifier)){
							Metodo MetPadre = claseActual.getMetodo(identifier);
							if (MetPadre.mismoTipo(new TVoid())==false){
								throw new RedefinitionMethodException(token.getLinea(),identifier,claseActual.getNombre());
							}
						}
					}
				}
			}
			// heredo el nuevo metodo pero no lo agrego a la clase actual hasta no asegurarme que no hay error en los argumentos
			Met = new Metodo(identifier,new TVoid());
			//SEMANTICO END
		}else{
			Tipo tipo = Type();			
			//SEMANTICO BEGIN 
			String identifier = token.getLexema();
			if (claseActual.existeMetodo(identifier)){
				throw new RepeatedMethodException(token.getLinea(),identifier);
			}else {
					if (claseActual.getNombre().equals(identifier)){
						throw new MethodClassNameException(token.getLinea(), identifier);
					}else{
						if (claseActual.getPadre().getEnClassDef()==true){
							if (claseActual.existeMetodoEnPadre(identifier)){
								Metodo MetPadre = claseActual.getMetodo(identifier);
								if (MetPadre.mismoTipo(tipo)==false){
									throw new RedefinitionMethodException(token.getLinea(),identifier,claseActual.getNombre());
								}
							}
						}
					}
				}
		
				// heredo el nuevo metodo pero no lo agrego a la clase actual hasta no asegurarme que no hay error en los argumentos
				Met = new Metodo(identifier,tipo);				
			//SEMANTICO END			
		}
		consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
		
		FormalArgs(Met);//<FormalArgs>.metodo = Met;
		consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
	}

	private void Super() throws LexicException, SintacticException, SemanticException{
		if (equalType(Token.TipoToken.EXTENDS)){
			consumirTokenCondicional(Token.TipoToken.EXTENDS);
			if (!(equalType(Token.TipoToken.IDENTIFICADOR))){
				// no encontro un identificador, error en declaracion del classDef
				cGen.close();
				al.getBuffer().cerrarArchivo();
				throw new IdentifierException(token.getLexema(), token.getLinea(),token.getColumna());
			}else{
				String identifier = token.getLexema();
				consumirToken();
				//SEMANTICO BEGIN
					if(claseActual.getNombre().equals(identifier)){
						throw new ExtendsSameClassException(token.getLinea(),identifier);}	
					if (!(tsG.existeClase(identifier))){
						TClase nuevaClase = new TClase(identifier,false);
						tsG.agregarClase(nuevaClase);
					}
					if (definicion == false){
						claseActual.setPadre(tsG.getClase(identifier));
					}else{
						if(!(claseActual.getPadre().equals(tsG.getClase(identifier)))){
							throw new ClassDeclaratedNotFoundException(token.getLinea(),identifier);
						}
					}
				//SEMANTICO END		
			}
		}else{ //termina el then osea vendria el lambda
			//SEMANTICO BEGIN
				if (definicion == false){
						claseActual.setPadre(tsG.getClase("Object"));
				}else{
					if(!(claseActual.getPadre().equals(tsG.getClase("Object")))){
						throw new ClassDeclaratedNotFoundException(token.getLinea(),claseActual.getNombre());
					}
				}
			//SEMANTICO END
		}
		
	}
		
	private void FormalArgs(Metodo metodo) throws LexicException, SintacticException, SemanticException{
		if (equalType(Token.TipoToken.PARABRE)){
			consumirToken();
			//SEMANTICO BEGIN
			posArg = 0;
			//SEMANTICO END
			while (!(equalType(Token.TipoToken.PARCIERRA))){
				    // tiene argumentos 
					FormalArgsList(metodo); //<NTFormalArgList>. metodo = <FormalArgs>.metodo 		
					if (!(equalType(Token.TipoToken.PARCIERRA)))
						consumirTokenCondicional(Token.TipoToken.COMA);
					    posArg = posArg +1;
			}	
			consumirTokenCondicional(Token.TipoToken.PARCIERRA);
			if (metodo.getDefinido()==false){
				 //SEMANTICO BEGIN
				if (claseActual.getPadre().getEnClassDef()==true){
					if (claseActual.existeMetodoEnPadre(metodo.getNombre())){
						Metodo metPadre = claseActual.getMetodo(metodo.getNombre());
						if (metPadre.getTableArg().size()!=metodo.getTableArg().size()){
							throw new NumberParametersException(token.getLinea());
						}
					}
				}
				 // recien cuando termino de verificar todos los argumentos formales agrego el metodo a la clase actual		
					claseActual.agregarMetodo(metodo);
					metodo.setDefinido(); // seteo definido de metodo en true
				//SEMANTICO END
			}
		}else{
			// no encontro un parentesis que abre, error en declaracion del classDef
			cGen.close();
			al.getBuffer().cerrarArchivo();
			throw new FormalArgsException(token.getLexema(), token.getLinea(),token.getColumna());
		}
	}
	
	private void FormalArgsList(Metodo metodo)throws LexicException, SintacticException, SemanticException{
		//SEMANTICO BEGIN
			if ((claseActual.getNombre().equals("Main")) && (metodo.getNombre().equals("Main")))	
				throw new ParameterlessConstructorException(token.getLinea());
		//SEMANTICO END		
		FormalArg(metodo);
		//SEMANTICO BEGIN
		//El offset de los parámetros debe ser en orden inverso al que fueron declarados y hay que sumarle 3.
		// por eso lo hacemos acá
			int offsetArg = metodo.getCantParametros()+3;
			for (int i=0; i < metodo.getCantParametros(); i++){
				Atributo atr = metodo.getTableArg().get(i);
				atr.setOffset(offsetArg);
				offsetArg--;
			}
		//SEMANTICO END
	}
	
	private void FormalArg(Metodo metodo)throws LexicException, SintacticException, SemanticException{
		Tipo tipo = Type();
		if (!(equalType(Token.TipoToken.IDENTIFICADOR))){
			// no encontro un identificador, error en declaracion del classDef
			cGen.close();
			al.getBuffer().cerrarArchivo();
			throw new IdentifierException(token.getLexema(), token.getLinea(),token.getColumna());
		}
		String identifier = token.getLexema();
		consumirToken();
		//SEMANTICO BEGIN  
			if (definicion == false) {
				if (claseActual.getPadre().getEnClassDef()==true){
					if (claseActual.existeMetodoEnPadre(metodo.getNombre())){
						Metodo metPadre = claseActual.getMetodo(metodo.getNombre());	
						if ((metPadre.getTableArg().size()==0) || ((posArg+1)>metPadre.getTableArg().size())){
							throw new NumberParametersException(token.getLinea(),identifier);
						}else{
							Atributo atrPadre = metPadre.getAtributo(posArg);		
							if (!(tipo.tipoToString().equals(atrPadre.getTipo().tipoToString()))){
								throw new ParameterRedefinitionException(token.getLinea(),identifier);
							}
						}
					}
				}
				Atributo newAtr = new Atributo(identifier,tipo);
				if (metodo.existeAtributo(newAtr)){
					throw new MoreThanOneParameterException(token.getLinea());
				}else{
					metodo.agregarAtributo(newAtr);
				}
			}else{
				if (posArg<metodo.getCantParametros()){
					Atributo newAtr = metodo.getAtributo(posArg);
					if (newAtr.getTipo().tipoToString().equals(tipo.tipoToString())){
						if (!(newAtr.getNombre().equals(identifier))){
						    throw new NameAtributeIncompalibleException(token.getLinea(),newAtr.getNombre());
						}
					}else{
						throw new TypeAtributeIncompalibleException(token.getLinea(),newAtr.getNombre());
					}
				}else{
					throw new MoreAtributeException(token.getLinea(),metodo.getNombre());
				}
			}
		//SEMANTICO END
		
	}
	
	private Tipo Type()throws LexicException, SintacticException, SemanticException{
		if (!((equalType(Token.TipoToken.BOOLEAN))||(equalType(Token.TipoToken.CHAR))||(equalType(Token.TipoToken.INT))||(equalType(Token.TipoToken.STRING))|| (equalType(Token.TipoToken.IDENTIFICADOR)))){
			// no encontro un tipo primitivo antes del identificador
			cGen.close();
			al.getBuffer().cerrarArchivo();
			throw new primitiveTypeException(token.getLexema(), token.getLinea(),token.getColumna());
		}else{
			String identifier = token.getLexema(); 
			consumirToken();
			if (identifier.equals("boolean")) {return new Booleano();}
			else
				if (identifier.equals("char")) {return new Caracter();}
				else
					if (identifier.equals("int")) {return new Entero();}
					else
						if (identifier.equals("string")) {return new Cadena();}
						else{	
							// TENGO QUE VER SI EXISTE LA CLASE PRIMERO!!!!!!!!!
							if (!(tsG.existeClase(identifier))){
								if (definicion==true){
									throw new RetClassNoFoundException(token.getLinea(),identifier);
								}else{
									TClase nuevaClase = new TClase(identifier,false);
									tsG.agregarClase(nuevaClase);
									return tsG.getClase(identifier);
								}
							}else{
								return tsG.getClase(identifier);
							}
						}
		}
			
	}
	
	private Tipo PrimitiveType()throws LexicException, SintacticException{
		if (!((equalType(Token.TipoToken.BOOLEAN))||(equalType(Token.TipoToken.CHAR))||(equalType(Token.TipoToken.INT))||(equalType(Token.TipoToken.STRING)))){
			// no encontro un tipo primitivo antes del identificador
			cGen.close();
			al.getBuffer().cerrarArchivo();
			throw new primitiveTypeException(token.getLexema(), token.getLinea(),token.getColumna());
		}else{
			String identifier = token.getLexema();
			consumirToken();
			if (identifier.equals("boolean")) {return new Booleano();}
			else
				if (identifier.equals("char")) {return new Caracter();}
				else
					if (identifier.equals("int")) {return new Entero();}
					else
						return tsG.getClase(identifier);	
		}
	}	
	
/******************************************************** classDef ya tiene todos los chequeos semanticos *******************************************/	
	
	/* CLASS: */
	private void chequearMetodos(TClase clase) throws LexicException, SintacticException,SemanticException{
		Enumeration<String> e = clase.getTablaMetodo().keys();  
        String nameMetodo;  
        Metodo metodo, metPadre;
        while (e.hasMoreElements()) {  
           nameMetodo = e.nextElement(); 
           metodo = clase.getTablaMetodo().get(nameMetodo);
           if (clase.getPadre().getEnClassDef()!=false){
	           if (clase.existeMetodoEnPadre(nameMetodo)){
	        	   metPadre = clase.getPadre().getMetodo(nameMetodo);
	        	   if (metodo.mismoTipo(metPadre.getTipo())==false){
	        		   throw new RedefinitionMethodException(token.getLinea(),metodo.getNombre(),clase.getNombre());
	        	   }else{
	        		   if (metodo.getCantParametros()!=metPadre.getCantParametros()){
	        			   throw new NumberParametersException(token.getLinea(),metodo.getNombre());
	        		   }else{
	        			   int index = 0;
	        			   while(index<metodo.getTableArg().size()){
	        				  Tipo tipo1 = metodo.getAtributo(index).getTipo();
	        				  Tipo tipo2 = metPadre.getAtributo(index).getTipo();
	        				  if (!(tipo1.tipoToString().equals(tipo2.tipoToString()))){
	        					  throw new TypeParametersException(token.getLinea(),metodo.getNombre(),metodo.getAtributo(index).getNombre());
	        				  }
	        				  index++;
	        			   }
	        		   }
	        	   }
	           }
           }else{
        	   throw new ClassNoFoundException(token.getLinea(),clase.getPadre().getNombre());
           }  
        }       
	}
	
	
	private void Class() throws LexicException, SintacticException,SemanticException{      
		//SEMANTICO BEGIN - controlo que en las clases declaradas exista la clase main
		definicion = true;
		if (tsG.tieneMain() == false)
			throw new UndeclaredClassMainException(token.getLinea());
		else {
			TClase claseMain = tsG.getClase("Main");
			if (!(claseMain.existeMetodo("Main") || claseMain.existeMetodo("main"))){
				throw new UndeclaredMainMethodException(token.getLinea());
			}
		}
		//SEMANTICO END
		
		if (equalType(Token.TipoToken.CLASS)){
			consumirToken();
			String identifier = token.getLexema();
			consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
			
			//SEMANTICO BEGIN
				if (!(tsG.existeClase(identifier))) 
					throw new NotDefineUndeclaredClassException(token.getLinea());
				else {
					claseActual = tsG.getClase(identifier);
					claseActual.setCantMetodos();
					if (claseActual.getDefinida() == true) {
						throw new DoubleDefinedClassException(token.getLinea());
					}
					claseActual.setDefinida(true);
					tsG.decrementarCantClases();
					claseActual.setTieneCtor(false);
				} 
			//SEMANTICO END
			
			Super();
			
			//*************************************************
	    	//GENERACIÓN CI
			cGen.generate(".DATA");
			String VT = "VT_"+identifier+": DW l"+claseActual.getNombre()+claseActual.getNombre()+", ";
			int index;
			TClase padre = claseActual.getPadre();
			while (!(padre.equals(tsG.getClase("Object")))){
				LinkedList<String> listMetodosPadre = padre.getListaMetodos();
				index = 0;
				while (index<listMetodosPadre.size()){	
					String nameMetPadre = listMetodosPadre.get(index);
					Metodo metPadre = padre.getMetodo(nameMetPadre);
					if ((claseActual.existeMetodo(metPadre.getNombre())==false) && (!(padre.getNombre().equals(metPadre.getNombre())))){
						VT = VT+"l"+metPadre.getNombre()+padre.getNombre()+", "; // devuelvo el metodo en la pos i de la tabla
					}else{
						if ((claseActual.existeMetodo(claseActual.getNombre())==true) && (!(claseActual.getNombre().equals(metPadre.getNombre())))){
							VT = VT+"l"+metPadre.getNombre()+claseActual.getNombre()+", "; // devuelvo el metodo en la pos i de la tabla
						}
					}
					index++;
				}
				padre = padre.getPadre();
			}
			
			LinkedList<String> listMetodos = claseActual.getListaMetodos();
			index = 0;
			while (index<listMetodos.size()){
				String nameMet = listMetodos.get(index);
				Metodo met = claseActual.getMetodo(nameMet);
				if ((!(claseActual.existeMetodoEnPadre(met.getNombre()))) && (!(claseActual.getNombre().equals(met.getNombre())))){
					VT = VT+"l"+met.getNombre()+identifier+", "; // devuelvo el metodo en la pos i de la tabla
				}
				index++;
			}
			
			VT = VT.substring(0,VT.length() - 2); // para eliminar la ultima ,
			cGen.generate(VT);
			cGen.generate(".CODE");
			//*************************************************
			
			consumirTokenCondicional(Token.TipoToken.LLAVEABRE);
			Cosas();
			consumirTokenCondicional(Token.TipoToken.LLAVECIERRA);
		    //*******************************************************************************************
			//GENERACIÓN CI
			// obtengo la clase
			TClase claseGen = tsG.getClase(identifier);

			cGen.generate(identifier+": NOP");
			cGen.generate("RMEM 1					;Reservamos memoria para el resultado del malloc (la referencia al nuevo CIR)");
			// la cantidad de variables de instancia de CIR de la clase 
			//(tanto las definidas como las heredadas)	
			// HAGO UN JUMP AL FINAL PARA LA INSTRUCCION PUSH PORQUE NO TENGO EL TAMAÑO DE TODOS LOS CIR CALCULADOS AUN
			String labelNew1 = lGen.generateLabel();
			String labelNewRet1 = lGen.generateLabel();
			cGen.generate("JUMP "+labelNew1); 
			cGen.generate(labelNewRet1+": NOP");
			InstructionClassGenerate newInstruction1 = new InstructionClassGenerate(claseGen,labelNew1,labelNewRet1,"PUSH");
			listClassInstruction.add(newInstruction1);
			cGen.generate("PUSH malloc");
			cGen.generate("CALL");
			cGen.generate("DUP");
			//Apilamos la dirección del comienzo de la VT
			cGen.generate("PUSH VT_"+claseGen.getNombre());
			cGen.generate("STOREREF 0");//se guarda la referencia a la VT en el CIR
			cGen.generate("DUP ");
		
			//Ya evaluamos los parámetros y acomodamos el this tenemos que apilar la dirección del código del constructor
			//A diferencia de los métodos, no usamos la VT para apilar la dirección del constructor, ya que se puede determinar en tiempo de compilación
			cGen.generate("PUSH "+"l"+claseActual.getNombre()+claseActual.getNombre()); //label del constructor, invocar al constructor
			cGen.generate("CALL ");	
			cGen.generate("POP");
			String labelNew2 = lGen.generateLabel();
			String labelNewRet2 = lGen.generateLabel();
			cGen.generate("JUMP "+labelNew2); 
			cGen.generate(labelNewRet2+": NOP");
			InstructionClassGenerate newInstruction2 = new InstructionClassGenerate(claseGen,labelNew2,labelNewRet2,"RET");
			listClassInstruction.add(newInstruction2);
		  //*******************************************************************************************
	
			//SEMANTICO BEGIN
				if (claseActual.getCantMetodos() != 0){
					throw new UndefinedMethodDeclararedException(token.getLinea(),claseActual.getNombre());
				}
			//SEMANTICO END
			
		}else{
			// error en declaracion del class
			cGen.close();
			al.getBuffer().cerrarArchivo();
			throw new classDeclarationException(token.getLexema(), token.getLinea(),token.getColumna());
		}
	}

   private void Cosas() throws LexicException, SintacticException,SemanticException{
	   if (equalType(Token.TipoToken.IDENTIFICADOR)){
		   String identifier = token.getLexema();
		   consumirToken();
		   FieldOCtorOMethod(identifier); //<FieldOCtorOMethod>.ID = identifier.getLexema()
	   }else{
		   if (equalType(Token.TipoToken.VOID)){
			   consumirToken();
			   String identifier = token.getLexema();
			   consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
			   
			  //SEMANTICO BEGIN
			   if (claseActual.existeVariable(identifier))
					throw new MethodVariableNameException(token.getLinea(),identifier);				   
			   if (!(claseActual.existeMetodo(identifier))){
					throw new NotDefineUndeclaredMethodException(token.getLinea(),identifier);
				}else {
					if (claseActual.getNombre().equals(identifier))
						throw new NotDefinedMethodClassNameException(token.getLinea(),identifier);
					else {
							Metodo metodo = claseActual.getMetodo(identifier);
							metActual = metodo;
							if (metodo.getTipo().tipoToString().equals("void")){
								claseActual.decrementarCantMetodos();
								RestoMethod(metodo); //<RestoMethod>.metodo = claseActual.getMetodo(identifier.getLexema())
							}else throw new IncompatibleReturnTypesException(token.getLinea(),identifier);
						}
					}
			  //SEMANTICO END			   
			   
		   }else{
			   Tipo tipo = PrimitiveType();
			   FieldOMethod(tipo.tipoToString());//{<FieldOMethod>.tipo = <PrimitiveType>.tipo}
		   }
	   }
   }

   private void FieldOCtorOMethod(String ID) throws LexicException, SintacticException,SemanticException{
	   Metodo metodo;
	   if (equalType(Token.TipoToken.PARABRE)){   
		   //*************************************************
	    	//GENERACIÓN CI
			cGen.generate(";ACA EMPIEZA EL METODO "+ID+" DE LA CLASE "+claseActual.getNombre());
			//En caso de tratarse de el Ctor de Main se le pone la etiqueta especial de comienzo.
			if (ID.equals("Main")){
				cGen.generate("lMainMain: LOADFP");
			}else{
				cGen.generate("l"+ID+claseActual.getNombre()+ ": LOADFP");
			}
			cGen.generate("LOADSP");
			cGen.generate("STOREFP");
		  //*************************************************
		  // SEMANTICO BEGIN 
		   		if (claseActual.existeVariable(ID))
		   			throw new MethodVariableNameException(token.getLinea(),ID);	
		   
		   		if (!(claseActual.existeMetodo(ID))) 
					throw new UndefinedConstructorDeclararedException(token.getLinea());
				else{
					if (!(claseActual.getNombre().equals(ID)))
						throw new DefinedInvalidNameConstructorException(token.getLinea());
						else{  //es un constructor correcto pero si claseMain es false y el nombre de la clase es main quiere decir que no de declaro el main antes
							if ((tsG.tieneMain() == false) && (ID.equals("Main"))){
								throw new InvalidDefinedConstructorMain(token.getLinea());
							}						
							metodo = claseActual.getMetodo(ID);
							metActual = metodo;
							claseActual.decrementarCantMetodos();
							
							claseActual.setTieneCtor(true); // es el primer constructor y ya dejamos asentado que lo leimos por si aparece otro mas adelante
							
						}
				}
			//SEMANTICO END
		   FormalArgs(metodo); //<FormalArgs>.metodo = claseActual.getMetodo(<FieldOCtorOMethod>.ID)
		   boolean tieneRet = Block(metodo); //<Block>.metodo = claseActual.getMetodo(<FieldOCtorOMethod>.ID)
		   if (metodo.getTipo().tipoToString().equals("void")){
				if (tieneRet==true)
					throw new RetorneSentenceMethodException(token.getLinea(),metodo.getNombre());
			}else{
				if (tieneRet==false){
					throw new ReturnMethodException(token.getLinea(),metodo.getNombre());
				}
			}
		   //*************************************************
	    	//GENERACIÓN CI
		     cGen.generate("STOREFP");
		     int ret = claseActual.getMetodo(ID).getCantParametros()+1;
			 cGen.generate("RET "+ret+"					;DE LA CLASE "+claseActual.getNombre()); 
		   //*************************************************
		   Ctor();
		   //SEMANTICO BEGIN
		   if (tieneRet == true){
			   throw new RetorneSentenceConstructorException(token.getLinea(),ID);
			}
		   //SEMANTICO END
		   
	   }else{
		   FieldOMethod(ID);  //<FieldOMethod>.tipo = <FieldOCtorOMethod>.ID 
	   }
   }

   private void FieldOMethod(String tipo) throws LexicException, SintacticException,SemanticException{
	   String identifier = token.getLexema();
	   consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
	   //no se sabe que es el identifier si es el nombre de un metodo o de una variable, hay que heredarlo para ver mas adelante
	   //<RestoFieldOMethod>.fieldOMethod = identifier.getLexema();
	   RestoFieldOMethod(identifier,tipo); //<RestoFieldOMethod>.tipo = <FieldOMethod>.tipo; 
   }
   
  private Tipo obtenerTipo(String tipo) throws LexicException, SintacticException,SemanticException{
	Tipo t;
	if (tipo.equals("boolean")) {t = new Booleano();}
	else
		if (tipo.equals("char")) {t = new Caracter();}
		else
			if (tipo.equals("int")) {t = new Entero();}
			else
				if (tipo.equals("String")) {t = new Cadena();}
				else{	
					if (tsG.existeClase(tipo)){
						t = tsG.getClase(tipo);
					}else{
						throw new InvalidVariableTypeException(token.getLinea(),tipo);
					}
				}
	return t;
  }

   private void RestoFieldOMethod(String ID, String tipo) throws LexicException, SintacticException,SemanticException{
	   Metodo metodo;
	   Tipo t = obtenerTipo(tipo);
	   if (equalType(Token.TipoToken.PARABRE)){
		   //SEMANTICO BEGIN 
		   if (claseActual.existeVariable(ID))
				throw new MethodVariableNameException(token.getLinea(),ID);	
		   else
		   		if (!(claseActual.existeMetodo(ID)))
					throw new NotDefineUndeclaredMethodException(token.getLinea(),ID);
				else
					if (claseActual.getNombre().equals(ID))
						throw new NotDefinedMethodClassNameException(token.getLinea(),ID);
						else{ 	
							metodo = claseActual.getMetodo(ID);
							metActual = metodo;
							if (metodo.mismoTipo(t)) {
								claseActual.decrementarCantMetodos();
							}else{ throw new IncompatibleReturnTypesException(token.getLinea(),t.tipoToString());}
						}
			//SEMANTICO END					
		 //*************************************************
	    	//GENERACIÓN CI	 
			cGen.generate(";ACA EMPIEZA EL METODO "+ID+" CLASE ACTUAL "+claseActual.getNombre());
			cGen.generate("l"+ID+claseActual.getNombre()+ ": LOADFP");
			cGen.generate("LOADSP");
			cGen.generate("STOREFP");
		   
		 //*************************************************  
		   FormalArgs(metodo); //<FormalArgs>.metodo = claseActual.getMetodo(<RestoFieldOMethod>.fieldOMethod)
		   boolean tieneRet = Block(metodo); //<Block>.metodo = claseActual.getMetodo(<RestoFieldOMethod>.fieldOMethod)
		   //SEMANTICO BEGIN
		   if (metodo.getTipo().tipoToString().equals("void")){
				if (tieneRet==true)
					throw new RetorneSentenceMethodException(token.getLinea(),metodo.getNombre());
			}else{
				if (tieneRet==false){
					throw new ReturnMethodException(token.getLinea(),metodo.getNombre());
				}
			}
		   //SEMANTICO END
		//*************************************************
	    	//GENERACIÓN CI	 
			int offsetRet;
			Metodo met = claseActual.getMetodo(ID);
			offsetRet=met.getCantParametros()+1; //el 1 es para tener en cuenta el this
			cGen.generate("STOREFP");
			cGen.generate("RET "+offsetRet+"					;del metodo "+met.getNombre());
		//*************************************************	
		   Methods();
	   }else{
		   //SEMANTICO BEGIN 
		   if (claseActual.existeVariable(ID)){
				throw new NotDefineUndeclaredVarException(token.getLinea());
		   }else{
			   if (claseActual.getNombre().equals(ID)){
					throw new  RepeatedVarClassException(token.getLinea());
				}
		   	}
			Variable var = new Variable(ID,t);
			claseActual.agregarVariable(var);
			//Pone el offset local y después al final le suman una constante en base al tamaño del CIR de los ancestros
			var.setOffsetLocal(claseActual);
		    //SEMANTICO END
		   RestoField(t); //<RestoField>.tipo  = <RestoFieldOMethod>.tipo
	   }
   }
  

   private void RestoField(Tipo t) throws LexicException, SintacticException,SemanticException{
	   if (equalType(Token.TipoToken.COMA)){
		   consumirToken();		   
		   VarDeclarationList(t);		   
		   //{<VarDeclarationList>.tipo = <RestoField>.tipo}
	   }
	   consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
	   Cosas();
   }

	private void Method() throws LexicException, SintacticException,SemanticException{
		Metodo metodo;
		Tipo tipo;
		if (equalType(Token.TipoToken.VOID)){
			tipo = new TVoid();
			consumirToken();
		}else{
			tipo = Type();
		}
		String identifier = token.getLexema();
		consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
		
		//SEMANTICO BEGIN
		if (claseActual.existeVariable(identifier)){
			throw new MethodVariableNameException(token.getLinea(),identifier);		
		}else{
			if (!(claseActual.existeMetodo(identifier))){
				throw new NotDefineUndeclaredMethodException(token.getLinea(),identifier);
			}else{
				if (claseActual.getNombre().equals(identifier))
					throw new NotDefinedMethodClassNameException(token.getLinea(),identifier);
					else {
						metodo = claseActual.getMetodo(identifier);
						metActual = metodo;
						if (metodo.mismoTipo(tipo)) 
							claseActual.decrementarCantMetodos();
						else throw new  IncompatibleReturnTypesException(token.getLinea(),tipo.tipoToString());
					}
			}
		}
		//SEMANTICO END	
	  //*************************************************
    	//GENERACIÓN CI
		cGen.generate(";ACA EMPIEZA EL METODO "+identifier+" DE LA CLASE "+claseActual.getNombre());
		cGen.generate("l"+identifier+claseActual.getNombre()+ ": LOADFP");
		cGen.generate("LOADSP");
		cGen.generate("STOREFP");
	  //*************************************************
		FormalArgs(metodo); //<FormalArgs>.metodo = metodo
		boolean tieneRet = Block(metodo);//<Block>.metodo = metodo
		//SEMANTICO BEGIN
		if (metodo.getTipo().tipoToString().equals("void")){
			if (tieneRet == true){
				throw new  NotExpectedValueRetException(token.getLinea());
			}
		}else{
			if (tieneRet == false){
				throw new  ExpectedValueRetException(token.getLinea(),tipo.tipoToString());
			}
		}
		//SEMANTICO END
	 //*************************************************
    	//GENERACIÓN CI
		//if (!(tipo.tipoToString().equals("void"))){ 
			int offsetRet;
			Metodo met = claseActual.getMetodo(identifier);
			offsetRet=met.getCantParametros()+1; //el 1 es para tener en cuenta el this
			cGen.generate("STOREFP");
			cGen.generate("RET "+offsetRet+"					;del metodo "+met.getNombre());
		//}
	//*************************************************
	}
	
	private void VarDeclarationList(Tipo tipo) throws LexicException, SintacticException,SemanticException{
		boolean seguir = true;
		do{	
			
			String identifier = token.getLexema();		
			consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
			//SEMANTICA BEGIN
			if (claseActual.existeVariable(identifier)){
				throw new  NotDefineUndeclaredVarException(token.getLinea());
			}else{
				if (claseActual.existeMetodo(identifier)){
					throw new  RepeatedVarMethodException(token.getLinea());
			
				}else{
					if (claseActual.getNombre().equals(identifier)){
							throw new  RepeatedVarClassException(token.getLinea());
						}else{
							
							Variable var =new Variable(identifier, tipo);
							claseActual.agregarVariable(var);
							//Pone el offset local y después al final le suman una constante en base al tamaño del CIR de los ancestros
							var.setOffsetLocal(claseActual);		
						}
					}
				}
			//SEMANTICA END
			if (equalType(Token.TipoToken.COMA)){
				consumirToken();
			}else{
				seguir = false;
			}
		} while(seguir);
	}  
	
	private void Ctor() throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.IDENTIFICADOR)){
			String nombOtipo = token.getLexema();
			consumirToken();
			//no se que es asi que paso el nombre del identifier, puede ser un metodo o un constructor
			CtorOMethod(nombOtipo); //<CtorOMethod>.nombOtipo = identifier.getLexema();
		}else{
			if (equalType(Token.TipoToken.VOID)){
				consumirToken();
				String identifier = token.getLexema();
				consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
				// CHEQUEO SEMANTICO BEGIN
				if (!(claseActual.existeMetodo(identifier))){
					throw new NotDefineUndeclaredMethodException(token.getLinea(),identifier);
				}else{
					if (identifier.equals(claseActual.getNombre())){
							throw new NotDefinedMethodClassNameException(token.getLinea(),identifier);
					}else {
							Metodo metodo = claseActual.getMetodo(identifier);
							metActual = metodo;
							if (metodo.mismoTipo(new TVoid())){
								claseActual.decrementarCantMetodos();
								RestoMethod(metodo);
							}else throw new IncompatibleReturnTypesException(token.getLinea(),metodo.getTipo().tipoToString());
					}
				}
				// CHEQUEO SEMANTICO END
				
			}else{
				if (!(equalType(Token.TipoToken.LLAVECIERRA))){
					Tipo tipo = PrimitiveType();
					String identifier = token.getLexema();
					consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
					// CHEQUEO SEMANTICO BEGIN
					//sabemos que es un metodo
					if (!(claseActual.existeMetodo(identifier))){
						throw new NotDefineUndeclaredMethodException(token.getLinea(),identifier);
					}else
						if (identifier.equals(claseActual.getNombre()))
							throw new NotDefinedMethodClassNameException(token.getLinea(),identifier);
						else {
							Metodo metodo = claseActual.getMetodo(identifier);
							metActual = metodo;
							if (metodo.mismoTipo(tipo)){ 
								claseActual.decrementarCantMetodos();
								RestoMethod(metodo);
							}else throw new IncompatibleReturnTypesException(token.getLinea(),tipo.tipoToString());
						}
					//CHEQUEO SEMANTICO END
				}
			}
		}
	}

	private void CtorOMethod(String nombOtipo) throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.IDENTIFICADOR)){
			String identifier = token.getLexema();
			consumirToken();
			// CHEQUEO SEMANTICO BEGIN
			if (!(claseActual.existeMetodo(identifier)))
				throw new NotDefineUndeclaredMethodException(token.getLinea(),identifier);
			else
				if (nombOtipo.equals(claseActual.getNombre()))
						throw new NotDefinedMethodClassNameException(token.getLinea(),identifier);
					else{ 	
						Metodo metodo = claseActual.getMetodo(identifier);
						metActual = metodo;
						if (metodo.mismoTipo(obtenerTipo(nombOtipo))){ 
							claseActual.decrementarCantMetodos();
							RestoMethod(metodo);
						}else throw new IncompatibleReturnTypesException(token.getLinea(),nombOtipo);
					}
			// CHEQUEO SEMANTICO END
		}else{
			// CHEQUEO SEMANTICO BEGIN
			if (claseActual.getTieneCtor())
				throw new DoubleConstructorException(token.getLinea(),nombOtipo);
				// no hace falta hacer nada porque la unica forma que entre a esta produccion es porque 
				// se intenta crear un nuevo constructor y por ende tira el error y corta
			// CHEQUEO SEMANTICO END
			Metodo met = new Metodo(nombOtipo,new TVoid());
			FormalArgs(met);
			boolean tieneRet = Block(met);
			if (met.getTipo().tipoToString().equals("void")){
				if (tieneRet==true)
					throw new RetorneSentenceMethodException(token.getLinea(),met.getNombre());
			}else{
				if (tieneRet==false){
					throw new ReturnMethodException(token.getLinea(),met.getNombre());
				}
			}
			Ctor();	
		}
	}

	private void RestoMethod(Metodo met) throws LexicException, SintacticException,SemanticException{
	  //*************************************************
    	//GENERACIÓN CI
		cGen.generate(";ACA EMPIEZA EL METODO "+met.getNombre());
		cGen.generate("l"+met.getNombre()+claseActual.getNombre()+ ": LOADFP");
		cGen.generate("LOADSP");
		cGen.generate("STOREFP");
	  //*************************************************
		// tanto a FormalArgs como a Block heredan el nombre del metodo para poder cargar los argumentos y el block respectivamente
		FormalArgs(met); //<FormalArgs>.metodo = <RestoMethod>.metodo
		boolean tieneRet = Block(met); //<Block>.metodo = <RestoMethod>.metodo
		if (met.getTipo().tipoToString().equals("void")){
			if (tieneRet==true)
				throw new RetorneSentenceMethodException(token.getLinea(),met.getNombre());
		}else{
			if (tieneRet==false){
				throw new ReturnMethodException(token.getLinea(),met.getNombre());
			}
		}
	  //*************************************************
    	//GENERACIÓN CI
		//if (!(met.getTipo().tipoToString().equals("void"))){
			int offsetRet;
			//Metodo met = claseActual.getMetodo(<FieldOCtorOMethod>.ID);
			offsetRet=met.getCantParametros()+1; //el 1 es para tener en cuenta el this
			cGen.generate("STOREFP");
			cGen.generate("RET "+offsetRet+"					; del metodo "+met.getNombre());
		//}
	  //*************************************************
		Methods();	
	}

	private void Methods() throws LexicException, SintacticException,SemanticException{
		if (!(equalType(Token.TipoToken.LLAVECIERRA))){
			Method();
			Methods();
		}
	}

	private boolean Block(Metodo metodo) throws LexicException, SintacticException,SemanticException{
		consumirTokenCondicional(Token.TipoToken.LLAVEABRE);
		//<NTStatement>.metodo = <Block>.metodo
		boolean tieneRet = NTStatement(metodo);
		//<Block>.tieneRet = <NTStatement>.tieneRet
		consumirTokenCondicional(Token.TipoToken.LLAVECIERRA);
		return tieneRet;
	}

	private boolean NTStatement(Metodo metodo) throws LexicException, SintacticException,SemanticException{	
		boolean tieneRet=false;
		while (!(equalType(Token.TipoToken.LLAVECIERRA))){
			tieneRet = (tieneRet || Statement(metodo,true)); //<Statement>.metodo = <NTStatement>.metodo
			//<NTStatement>.tieneRet = (<Statement>.tieneRet || <NTStatement1>.tieneRet)
		}	
		return tieneRet;
	}	
	
	private boolean Statement(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		boolean tieneRet = false;
		Tipo tipo;
		if (equalType(Token.TipoToken.IF)){
			typeSentence = "if";
			consumirToken();
			consumirTokenCondicional(Token.TipoToken.PARABRE);
			tipo = Expression(metodo,ladoIzq);
			consumirTokenCondicional(Token.TipoToken.PARCIERRA);
		  //*************************************************
	    	//GENERACIÓN CI
			String labelElse = lGen.generateLabel();
			cGen.generate("BF "+labelElse);
		  //*************************************************	
			//CHEQUEO SEMANTICO BEGIN
			if (!(tipo.tipoToString().equals("boolean"))){
				throw new ConditionIfException(token.getLinea());
			}else{
				tieneRet = Block(metodo); //<Bolck>.metodo = <Statement>.metodo
			}
			//CHEQUEO SEMANTICO END		
			if (equalType(Token.TipoToken.ELSE)){
				consumirTokenCondicional(Token.TipoToken.ELSE);
			  //*************************************************
		    	//GENERACIÓN CI
				String labelFinal = lGen.generateLabel();
				cGen.generate("JUMP "+labelFinal);
				cGen.generate(labelElse+": NOP");
			  //*************************************************
				boolean tieneRetElse = Block(metodo);
			  //*************************************************
		    	//GENERACIÓN CI
				 cGen.generate(labelFinal+": NOP");
			  //*************************************************
				tieneRet = tieneRet && tieneRetElse;
				if (!(metodo.getTipo().tipoToString().equals("void"))){
					if (tieneRet == false){
						throw new NotRetMethodException(token.getLinea());
					}
				}
				//<Block>.metodo = <SatementPrima>.metodo
				//<Statement>.tieneRet = (<Block>.tieneRet && <StatementPrima>.tieneRet)
			}else { 
				//^{<StatementPrima>.tipo = null; <StatementPrima>.tieneRet = false}
			   //*************************************************
		    	//GENERACIÓN CI
				cGen.generate(labelElse+": NOP");
			   //*************************************************
				tieneRet = false;  
			}
			typeSentence = "";
		}else{
			if (equalType(Token.TipoToken.RETURN)){
				typeSentence = "return";
				consumirToken();
				if (!(equalType(Token.TipoToken.PUNTOYCOMA))){		
					tipo = Expression(metodo,ladoIzq);//{<NTExpression>.metodo = <Statement>.metodo}
				   //*************************************************
			    	//GENERACIÓN CI
					  int offsetStore = 3+metodo.getCantParametros()+1; //3 por el enlace dinamico, el puntero de retorno y el this, y el 1 es el ret_val (celda reservada para el valor de retorno)
					  cGen.generate("STORE "+offsetStore);
					  cGen.generate("STOREFP");
					  int ret = metodo.getCantParametros()+1;
					  cGen.generate("RET "+ret+"					;retorno del metodo "+metodo.getNombre());
				   //*************************************************
					//System.out.println();
					consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
					//CHEQUEO SEMANTICO BEGIN
						if (tipo==null){
							if (!(metodo.getTipo().tipoToString().equals("void"))){
								throw new RetorneExpressionException(token.getLinea(),metodo.getTipo().tipoToString());
							}
						}else{
							if (!(tipo.conforma(metodo.getTipo()))){
								throw new RetorneExpressionException(token.getLinea(),metodo.getTipo().tipoToString());
							}
						}
						tieneRet = true;
	
					//CHEQUEO SEMANTICO END				
				}else{
					consumirToken();
				}
				typeSentence = "";
			}else{
				//{<Block>.metodo = <Statement>.metodo} <Block> {<Statement.tieneRet> = <Block.tieneRet>}
				if (equalType(Token.TipoToken.LLAVEABRE)){
					tieneRet = Block(metodo);
				}else{
					if (equalType(Token.TipoToken.WHILE)){
						typeSentence = "while";
						consumirToken();
						consumirTokenCondicional(Token.TipoToken.PARABRE);
					  //*************************************************
				    	//GENERACIÓN CI
						String labelSeguir=lGen.generateLabel();
						cGen.generate(labelSeguir+": NOP");
					  //*************************************************
						tipo = Expression(metodo,ladoIzq);//<Expression>.metodo = <Statement>.metodo
					  //*************************************************
				    	//GENERACIÓN CI
						String labelSalir=lGen.generateLabel();
						cGen.generate("BF "+labelSalir);
					  //*************************************************
						consumirTokenCondicional(Token.TipoToken.PARCIERRA);
						//CHEQUEO SEMANTICO BEGIN
							if (!(tipo.tipoToString().equals("boolean"))){
								throw new ConditionWhileException(token.getLinea());
							}else{
								//<Block>.metodo = <Statement>.metodo
								tieneRet = Block(metodo);//{<Statemten>.tieneRet = false}
								tieneRet = false; //{<Statemten>.tieneRet = false}
							}
						//CHEQUEO SEMANTICO END		
					  //*************************************************
					    //GENERACIÓN CI	
						cGen.generate("JUMP "+ labelSeguir);
						cGen.generate(labelSalir+": NOP");	
					  //*************************************************
						typeSentence = "";
					}else{
						if (equalType(Token.TipoToken.FOR)){
							typeSentence = "for";
							consumirToken();
							consumirTokenCondicional(Token.TipoToken.PARABRE);
							tipo = Expression(metodo,ladoIzq);//{<Expression1>.metodo = <Statement>.metodo}
							//CHEQUEO SEMANTICO BEGIN
							if (!(tipo.tipoToString().equals("int"))){
								throw new ConditionForException(token.getLinea(),tipo.tipoToString());
							}
							//CHEQUEO SEMANTICO END		
						  //*************************************************
						    //GENERACIÓN CI	
							String SALIRFor = lGen.generateLabel();
							String PPIOFor = lGen.generateLabel();
							cGen.generate(PPIOFor+": NOP");	
						  //*************************************************
							consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
							tipo = Expression(metodo,ladoIzq);//{<Expression2>.metodo = <Statement>.metodo}
						    //CHEQUEO SEMANTICO BEGIN
							if (!(tipo.tipoToString().equals("boolean"))){
								throw new ConditionForException(token.getLinea(),tipo.tipoToString());
							}
							//CHEQUEO SEMANTICO END				
							consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
						   //*************************************************
						    //GENERACIÓN CI	
							cGen.generate("BF "+SALIRFor);
						   //*************************************************
							tipo = Expression(metodo,ladoIzq);//{<Expression3>.metodo = <Statement>.metodo}
							//CHEQUEO SEMANTCIO BEGIN
							if (!(tipo.tipoToString().equals("int"))){
								throw new ConditionForException(token.getLinea(),tipo.tipoToString());
							}							
							//CHEQUEO SEMANTICO END
							consumirTokenCondicional(Token.TipoToken.PARCIERRA);
							tieneRet = Block(metodo);//{<Block>.metodo = <Statement>.metodo}
						  //*************************************************
						    //GENERACIÓN CI
							cGen.generate("JUMP "+PPIOFor);
							cGen.generate(SALIRFor+": NOP");
						  //*************************************************
							tieneRet = false; //{<Statement>.tieneRet = false}
							typeSentence = "";
						}else{				
							if (equalType(Token.TipoToken.PUNTOYCOMA)){
								consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
							}else{
								Tipo t = Expression(metodo,ladoIzq);//<Expression>.metodo = <Statement>.metodo		
								tieneRet = false; //<Statement>.tieneRet = false
								//System.out.println();
								consumirTokenCondicional(Token.TipoToken.PUNTOYCOMA);
							}
						}
					}
				}
			}
		}
	return tieneRet;
	}

	private Tipo Expression(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		//{<Expression>.tipo = <Assignament>.tipo}
		Tipo tipo = Assignament(metodo,ladoIzq);	
		return tipo; //{<Assignament>.metodo = <Expression>.metodo}
	}

	private Tipo Assignament(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		//{<ConditionalExpression>.ladoIzq = true;
		//<ConditionalExpression>.metodo = <Assignament>.metodo;}
		ExpressionSintetizados sintetizadosCond = ConditionalExpression(metodo, ladoIzq);
		Tipo resultado = null;	
	
		if (!(equalType(Token.TipoToken.PUNTOYCOMA))){
			if (equalType(Token.TipoToken.ASIG)){
				consumirTokenCondicional(Token.TipoToken.ASIG);
				//CHEQUEO SEMANTICO BEGIN
				if (sintetizadosCond.getAsignable() == false){
					throw new NotAssignableExpressionException(token.getLinea());
				}
				//<Expression>.metodo = <AssignamentPrima>.metodo;
				ladoIzq = false; //<Expression>.ladoIzq = false;
				//CHEQUEO SEMANTICO END
				Tipo tipoExp = Expression(metodo,ladoIzq);	
				//CHEQUEO SEMANTICO BEGIN
				if (tipoExp==null){
					   resultado = sintetizadosCond.getTipoExpression(); //<Assignament>.tipo = <ConditionalExpression>.tipo
				}else{
					if (tipoExp.conforma(sintetizadosCond.getTipoExpression())){              //<AssignamentPrima>.tipo.conforma(<ConditionalExpression>.tipo
						resultado = sintetizadosCond.getTipoExpression(); //<Assignament>.tipo = <ConditionalExpression>.tipo
					}else{
						throw new NotConformsException(token.getLinea(),tipoExp.tipoToString(),sintetizadosCond.getTipoExpression().tipoToString());
					}
				}
				//CHEQUECO SEMANTICO END
			  //*******************************************************************************************
				//GENERACIÓN CI
				// aca solo entra si es asignable si no ya tira error semantico
			    // VOY A NECESITAR HEREDAR EL ID DEL OBJ PARA SABER EL OFFSET	
				cGen.generate("DUP");
				//ES ATRIBUTO (parametro del metodo)
				Atributo atr = metodo.obtenerAtributo(sintetizadosCond.getID());
				if (atr!=null){
					//es un parámetro
					cGen.generate("STORE "+atr.getOffset()+"					;offset del atr "+atr.getNombre()+" en el RA");	 
				}else{
					// ES VARIABLE DE INSTANCIA 
					if (claseActual.existeVariable(sintetizadosCond.getID())){
						Variable var = claseActual.getVariable(sintetizadosCond.getID());
						cGen.generate("LOAD 3					;Apilamos el this "+sintetizadosCond.getID()); 
						//Invertimos el orden porque el STOREREF usa los parámetros en orden inverso (1° CIR, 2° valor a almacenar)
						cGen.generate("SWAP");
						// HAGO UN JUMP AL FINAL PARA LA INSTRUCCION STOREREF PORQUE NO TENGO EL OFFSET DE LA VARIABLE CALCULADO AUN
						String labelAssig = lGen.generateLabel();
						String labelAssigRet = lGen.generateLabel();
						cGen.generate("JUMP "+labelAssig); 
						cGen.generate(labelAssigRet+": NOP");
						InstructionGenerate newInstruction = new InstructionGenerate(var,labelAssig,labelAssigRet,"STOREREF");
						listInstruction.add(newInstruction);
					}
				}
				//cGen.generate("SWAP");
				cGen.generate("POP");
			  //*******************************************************************************************
			}else{
				resultado = sintetizadosCond.getTipoExpression();
			}
		}else{
				resultado = sintetizadosCond.getTipoExpression();
		}

		return resultado;
	}

	private ExpressionSintetizados ConditionalExpression(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		Tipo tipoRet;
		//<ConditionalAndExpression>.metodo = <ConditionalExpression>.metodo;
		//<ConditionalAndExpression>.ladoIzq = <ConditionalExpression>.ladoIzq;
		ExpressionSintetizados condAndExp = ConditionalAndExpression(metodo, ladoIzq);
		//<ConditionalExpressionPrima>.metodo = <ConditionalExpression>.metodo;
		//<ConditionalExpressionPrima>.ladoIzq = <ConditionalAndExpression>.ladoIzq;
		ExpressionSintetizados condExpPrima = ConditionalExpressionPrima(metodo,ladoIzq);		
		//CHEQUEO SEMANTICO BEGIN
		if (condExpPrima.getTipoExpression()==null){						//<ConditionalAndExpressionPrima>.tipo == null
			tipoRet = condAndExp.getTipoExpression();//<ConditionalExpression>.tipo = <ConditionalAndExpression>.tipo
		}else{
			if (!(condExpPrima.getTipoExpression().tipoToString().equals("boolean"))){
				throw new ConditionalExpressionException(token.getLinea(),condExpPrima.getTipoExpression().tipoToString());
			}else{
				tipoRet = condAndExp.getTipoExpression();//<ConditionalExpression>.tipo = <ConditionalAndExpression>.tipo
			}
		}
		//<ConditionalExpression>.asignable = (<ConditionalAndExpression>.asignable && <ConditionalExpressionPrima>.asignable);
		boolean asignable = (condAndExp.getAsignable()&& condExpPrima.getAsignable());
		String id;
		if (asignable==true){
			//sintetizo el id para obtener luego el offset correspondiente
			id = condAndExp.getID(); //<ConditionalExpression>.id = <ConditionalAndExpression>.id;
		}else{
			id = "";
		}
		//CHEQUEO SEMANTICO END
		//Como en Decaf tenemos asignaciones múltiples debemos duplicar el valor evaluado para que quede en la pila
		//cGen.generate("DUP");
		return new ExpressionSintetizados(ladoIzq,asignable,tipoRet,id);
	}
	
	private ExpressionSintetizados ConditionalExpressionPrima(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.OR)){
			consumirTokenCondicional(Token.TipoToken.OR);
			ExpressionSintetizados condAndExp = ConditionalAndExpression(metodo,ladoIzq);
			//CHEQUEO SEMANTICO BEGIN
			if (!(condAndExp.getTipoExpression().tipoToString().equals("boolean"))){
				throw new ConditionalExpressionException(token.getLinea(),condAndExp.getTipoExpression().tipoToString());
			}
			//CHEQUEO SEMANTICO END
		  //*******************************************************************************************
		    //GENERACIÓN CI
			cGen.generate("OR");
		  //*******************************************************************************************
			ExpressionSintetizados condExpPrima = ConditionalExpressionPrima(metodo,ladoIzq);
			//<ConditionalExpressionPrima>.tipo = <ConditionalExpressionPrima1>.tipo;
			//<ConditionalExpressionPrima>.asignable = false;
			return (new ExpressionSintetizados(ladoIzq,false,condExpPrima.getTipoExpression(),""));
		}else{
			return (new ExpressionSintetizados(ladoIzq,true,null,""));
		}
	}

	private ExpressionSintetizados ConditionalAndExpression(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		ExpressionSintetizados equalityExp = EqualityExpression(metodo,ladoIzq);	
		ExpressionSintetizados condAndExp = ConditionalAndExpressionPrima(metodo,ladoIzq);	
		//CHEQUEO SEMANTICO BEGIN
		Tipo tipoRet;
		
		if (condAndExp.getTipoExpression()==null){
			tipoRet = equalityExp.getTipoExpression(); //<ConditionalAndExpression>.tipo = <EqualityExpression>.tipo
		}else{
			if (!(condAndExp.getTipoExpression().tipoToString().equals("boolean"))){
				throw new ConditionalExpressionException(token.getLinea(),condAndExp.getTipoExpression().tipoToString());
			}else{
				tipoRet = condAndExp.getTipoExpression(); //<ConditionalAndExpression>.tipo = <ConditionalAndExpressionPrima>.tipo
			}
		}
		//<ConditionalAndExpression>.asignable = (<EqualityExpression>.asignable && <ConditionalAndExpressionPrima>.asignable);
		boolean asignable = (equalityExp.getAsignable() && condAndExp.getAsignable());
		String identifier;
		if (asignable == true){
			//sintetizo el id para obtener el offset luego
			identifier = equalityExp.getID();
		}else{
			identifier = "";
		}
		//CHEQUEO SEMANTICO END
		return (new ExpressionSintetizados(ladoIzq,asignable,tipoRet,identifier));
	}

	private ExpressionSintetizados ConditionalAndExpressionPrima(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.AND)){
			consumirTokenCondicional(Token.TipoToken.AND);
			ExpressionSintetizados equalityExp = EqualityExpression(metodo,ladoIzq);
			//CHEQUEO SEMANTICO BEGIN
			if (!(equalityExp.getTipoExpression().tipoToString().equals("boolean"))){
				throw new ConditionalExpressionException(token.getLinea(),equalityExp.getTipoExpression().tipoToString());
			}
			//CHEQUEO SEMANTICO END
		  //*******************************************************************************************
			//GENERACIÓN CI
			cGen.generate("AND");
		  //*******************************************************************************************
			ExpressionSintetizados condAndExpPrima = ConditionalAndExpressionPrima(metodo,ladoIzq);
			//<ConditionalAndExpressionPrima>.tipo = <EqualityExpression>.tipo;
			//<ConditionalAndExpressionPrima>.asignable = false;
			return (new ExpressionSintetizados(ladoIzq,false,equalityExp.getTipoExpression(),""));
		}else{
			//<ConditionalAndExpressionPrima>.tipo = null;
			//<ConditionalAndExpressionPrima>.asignable = true;
			return (new ExpressionSintetizados(ladoIzq,true,null,""));
		}
	}

	private ExpressionSintetizados EqualityExpression(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		ExpressionSintetizados relExp = RelationalExpression(metodo,ladoIzq);
		ExpressionSintetizados equalityExpPrima = EqualityExpressionPrima(metodo,ladoIzq);
		//CHEQUEO SEMANTICO BEGIN
		Tipo tipo;
		if (equalityExpPrima.getTipoExpression() != null){
			if ((!(equalityExpPrima.getTipoExpression().conforma(relExp.getTipoExpression()))) && (!(relExp.getTipoExpression().conforma(equalityExpPrima.getTipoExpression())))){	
				throw new IncompatibilityEqualityTypeException(token.getLinea());
			}else{
				//<EqualityExpression>.tipo = new Booleano();
				tipo = new Booleano();
			}
		}else{
			//<EqualityExpression>.tipo = <RelationalExpression1>.tipo
			tipo = relExp.getTipoExpression();
		}
		//<EqualityExpression>.asignable = (<RelationalExpression1>.asignable && <EqualityExpressionPrima>.asignable);
		boolean asignable = (relExp.getAsignable() && equalityExpPrima.getAsignable());
		String identifier;
		if (asignable){
			identifier = relExp.getID();
		}else{
			identifier = "";
		}
		//CHEQUEO SEMANTICO END
		return new ExpressionSintetizados(ladoIzq,asignable,tipo,identifier);
	}

	private ExpressionSintetizados EqualityExpressionPrima(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if ((equalType(Token.TipoToken.IGUAL)) || (equalType(Token.TipoToken.DISTINTO))){
			String operador = token.getLexema();
			consumirToken();
			ExpressionSintetizados relExp = RelationalExpression(metodo,ladoIzq);
		  //*******************************************************************************************
			//GENERACIÓN CI
			if (operador.equals("==")) cGen.generate("EQ");
			else cGen.generate("NE");
		  //*******************************************************************************************
			ExpressionSintetizados equalityExpPrima = EqualityExpressionPrima(metodo,ladoIzq);
			Tipo tipo;
			if (equalityExpPrima.getTipoExpression()==null){
				tipo = relExp.getTipoExpression();    //<EqualityExpressionPrima>.tipo = <RelationalExpression>.tipo
			}else{
				if (relExp.getTipoExpression().conforma(equalityExpPrima.getTipoExpression())){
					 tipo = equalityExpPrima.getTipoExpression();//<EqualityExpressionPrima>.tipo = <EqualityExpressionPrima1>.tipo
				}else{
					if (equalityExpPrima.getTipoExpression().conforma(relExp.getTipoExpression())){
						tipo = relExp.getTipoExpression();//<EqualityExpressionPrima>.tipo = <RelationalExpression>.tipo
					}else{
						throw new IncompatibilityEqualityTypeException(token.getLinea());
					}
				}
			}
			//<EqualityExpressionPrima>.asignable = false;
			return (new ExpressionSintetizados(ladoIzq,false,tipo,""));
		}else{
			return (new ExpressionSintetizados(ladoIzq,true,null,""));
		}
	}

	private ExpressionSintetizados RelationalExpression(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{	
		ExpressionSintetizados addExp = AdditiveExpression(metodo,ladoIzq);	
		ExpressionSintetizados relExpPrima = RelationalExpressionPrima(metodo,ladoIzq);
		//CHEQUEO SEMANTICO BEGIN
		Tipo tipo;
		if (relExpPrima.getTipoExpression() == null){
			tipo = addExp.getTipoExpression();//<RelationalExpression>.tipo = <AdditiveExpression>.tipo
		}else{
			if ((relExpPrima.getTipoExpression().tipoToString().equals("int")) && (addExp.getTipoExpression().tipoToString().equals("int"))){
				tipo = new Booleano();
			}else{
				throw new BinaryExpressionException(token.getLinea());
			}	
		}
		//<RelationalExpression>.asignable = (<AdditiveExpression>.asignable && <RelationalExpressionPrima>.asignable);
		boolean asignable = (addExp.getAsignable() && relExpPrima.getAsignable());
		String identifier;
		if (asignable == true){
			identifier = addExp.getID();//<RelationalExpression>.id = <AdditiveExpression>.id
		}else{
			identifier = "";
		}
		//CHEQUEO SEMANTICO END
		return (new ExpressionSintetizados(ladoIzq,asignable,tipo,identifier));
	}

	private ExpressionSintetizados RelationalExpressionPrima(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if ((equalType(Token.TipoToken.MENOR)) || (equalType(Token.TipoToken.MAYOR))|| (equalType(Token.TipoToken.MAYORYIGUAL)) || (equalType(Token.TipoToken.MENORYIGUAL))){
			String operador = token.getLexema();
			consumirToken();
			ExpressionSintetizados addExp = AdditiveExpression(metodo,ladoIzq);
			//CHEQUEO SEMANTICO BEGIN
			if (!(addExp.getTipoExpression().tipoToString().equals("int"))){
				throw new BinaryExpressionException(token.getLinea());
			}
			//CHEQUEO SEMANTICO END
		  //*******************************************************************************************
			//GENERACIÓN CI
			if (operador.equals("<")) cGen.generate("LT");
			else if (operador.equals(">")) cGen.generate("GT");
			else if (operador.equals("<=")) cGen.generate("LE");
			else cGen.generate("GE");
		  //*******************************************************************************************
			ExpressionSintetizados relExpPrima = RelationalExpressionPrima(metodo,ladoIzq);
			//<RelationalExpressionPrima>.tipo = <AdditiveExpression>.tipo;
			//<RelationalExpressionPrima>.asignable = false;
			return (new ExpressionSintetizados(ladoIzq,false,addExp.getTipoExpression(),""));
		}else{
			//<RelationalExpressionPrima>.tipo = null; <RelationalExpressionPrima>.asignable = true;
			return (new ExpressionSintetizados(ladoIzq,true,null,""));
		}
	}
	
	private ExpressionSintetizados AdditiveExpression(Metodo metodo, boolean ladoIzq)throws LexicException, SintacticException,SemanticException{
		ExpressionSintetizados multExp = MultiplicativeExpression(metodo,ladoIzq);
		ExpressionSintetizados addExpPrima = AdditiveExpressionPrima(metodo,ladoIzq);
		//CHEQUEO SEMANTICO BEGIN
		Tipo tipo;
		if ((multExp.getTipoExpression().tipoToString().equals("int")) || (addExpPrima.getTipoExpression()==null)){
			tipo = multExp.getTipoExpression();//<AdditiveExpression>.tipo = <MultiplicativeExpression>.tipo
		}else{
			throw new BinaryExpressionException(token.getLinea());
		}
		//<AdditiveExpression>.asignable = (<MultiplicativeExpression>.asignable && <AdditiveExpressionPrima>.asignable);
		boolean asignable = (multExp.getAsignable() && addExpPrima.getAsignable());
		String identifier;
		if (asignable==true){
			identifier = multExp.getID();
		}else{
			identifier = "";
		}
		//CHEQUEO SEMANTICO END
		return new ExpressionSintetizados(ladoIzq,asignable,tipo,identifier);
	}

	private ExpressionSintetizados AdditiveExpressionPrima(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if ((equalType(Token.TipoToken.MAS)) || (equalType(Token.TipoToken.MENOS))){
			String operador = token.getLexema();
			consumirToken();
			ExpressionSintetizados multExp = MultiplicativeExpression(metodo,ladoIzq);
			//CHEQUEO SEMANTICO BEGIN
			if (!(multExp.getTipoExpression().tipoToString().equals("int"))){
				throw new BinaryExpressionException(token.getLinea());
			}
			//CHEQUEO SEMANTICO END
		  //*******************************************************************************************
			//GENERACIÓN CI
			if (operador.equals("+")) cGen.generate("ADD");
			else cGen.generate("SUB");
		  //*******************************************************************************************
			ExpressionSintetizados addExpPrima = AdditiveExpressionPrima(metodo,ladoIzq);
			//<AdditeExpressionPrima>.tipo = <MultiplicativeExpression>.tipo;
			//<AdditeExpressionPrima>.asignable = false;
			return (new ExpressionSintetizados(ladoIzq,false,multExp.getTipoExpression(),""));
		}else{
			//<AdditiveExpressionPrima>.tipo = null; <AdditiveExpressionPrima>.asignable = true;
			return (new ExpressionSintetizados(ladoIzq,true,null,""));
		}
	}

	private ExpressionSintetizados MultiplicativeExpression(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		ExpressionSintetizados unaryExp = UnaryExpression(metodo,ladoIzq);
		ExpressionSintetizados multExpPrima = MultiplicativeExpressionPrima(metodo,ladoIzq);
		//CHEQUEO SEMANTICO BEGIN
		Tipo tipo;
		if ((unaryExp.getTipoExpression().tipoToString().equals("int")) || (multExpPrima.getTipoExpression()==null)){
			tipo = unaryExp.getTipoExpression(); //<MultiplicativeExpression>.tipo = <UnaryExpression>.tipo
		}else{
			throw new BinaryExpressionException(token.getLinea());
		}
		//<MultiplicativeExpression>.asignable = (<UnaryExpression>.asignable && <MultiplicativeExpressionPrima>.asignable);
		boolean asignable = (unaryExp.getAsignable() && multExpPrima.getAsignable());
		String identifier;
		if (asignable==true){
			identifier = unaryExp.getID();
		}else{
			identifier = "";
		}
		//CHEQUEO SEMANTICO END
		return new ExpressionSintetizados(ladoIzq,asignable,tipo,identifier);
	}

	private ExpressionSintetizados MultiplicativeExpressionPrima(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if ((equalType(Token.TipoToken.PRODUCTO)) || (equalType(Token.TipoToken.DIV)) || (equalType(Token.TipoToken.PORCENT))){
			String operador = token.getLexema();
			consumirToken();
			ExpressionSintetizados unaryExp = UnaryExpression(metodo,ladoIzq);
			//CHEQUEO SEMANTICO BEGIN
			if (!(unaryExp.getTipoExpression().tipoToString().equals("int"))){
				throw new BinaryExpressionException(token.getLinea());
			}
			//CHEQUEO SEMANTICO END
		  //*******************************************************************************************
			//GENERACIÓN CI
			if (operador.equals("*")) cGen.generate("MUL");
			else if (operador.equals("/")) cGen.generate("DIV");
			else cGen.generate("MOD");
		  //*******************************************************************************************
			ExpressionSintetizados multExpPrima = MultiplicativeExpressionPrima(metodo,ladoIzq);
			//<MultiplicativeExpressionPrima>.tipo = <UnaryExpression>.tipo;
			//<MultiplicativeExpressionPrima>.asignable = false;
			return new ExpressionSintetizados(ladoIzq,false,unaryExp.getTipoExpression(),"");
		}else{
			//<MultiplicativeExpressionPrima>.tipo = null; <MultiplicativeExpressionPrima>.asignable = true;
			return new ExpressionSintetizados(ladoIzq,true,null,"");
		}
	}

	private ExpressionSintetizados UnaryExpression(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if ((equalType(Token.TipoToken.MAS)) || (equalType(Token.TipoToken.MENOS)) || (equalType(Token.TipoToken.NOT))){
			String operador = token.getLexema();
			consumirToken();
			ExpressionSintetizados unaryExp = UnaryExpression(metodo,ladoIzq);
			//CHEQUEO SEMANTICO BEGIN
			Tipo tipo;
			if ((operador.equals("+")) ||(operador.equals("-"))){
				if (unaryExp.getTipoExpression().tipoToString().equals("int")){
					tipo = new Entero();
				}else	throw new UnaryExpressionIntException(token.getLinea());	
			}else{
					if (unaryExp.getTipoExpression().tipoToString().equals("boolean")){
						tipo = new Booleano();
					}else{ 	
						throw new UnaryExpressionBooleanException(token.getLinea());
					}
			}
			//<UnaryExpression>.asignable = false;
			//CHEQUEO SEMANTICO END
		  //*******************************************************************************************
			//GENERACIÓN CI	
			if (operador.equals("-")) cGen.generate("NEG");
			else if (operador.equals("!")) cGen.generate("NOT");
		  //*******************************************************************************************
			return new ExpressionSintetizados(ladoIzq,false,tipo,"");
		}else{
			//<Primary>.metodo = <UnaryExpression>.metodo; 
			//<Primary>.ladoIzq = <UnaryExpression>.ladoIzq; 
			//<Primary>.clase = null;
			ExpressionSintetizados primary = Primary(metodo,ladoIzq,null);
			//CHEQUEO SEMANTICO BEGIN
			//<UnaryExpression>.tipo = <Primary>.tipo;
			//<UnaryExpression>.asignable = <Primary>.asignable;
			boolean asignable = primary.getAsignable();
			String identifier;
			if (asignable==true){
				identifier = primary.getID();
			}else{
				identifier = "";
			}
			//CHEQUEO SEMANTICO END
			return new ExpressionSintetizados(ladoIzq,asignable,primary.getTipoExpression(),identifier);
		}
	}

	private ExpressionSintetizados Primary(Metodo metodo, boolean ladoIzq, Tipo clase) throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.THIS)){
				consumirToken();
				ExpressionSintetizados primaryThis = PrimaryThis(metodo,ladoIzq);
				// CHEQUEO SEMANTICO BEGIN
				//<Primary>.tipo = <PrimaryThis>.tipo;
				//<Primary>.asignable = <PrimaryThis>.asignable;
				String identifier;
				if (primaryThis.getAsignable()==true){
					identifier = primaryThis.getID();
				}else{
					identifier = "";
				}
				//CHEQUEO SEMANTICO END
				return (new ExpressionSintetizados(ladoIzq,primaryThis.getAsignable(),primaryThis.getTipoExpression(),identifier));
			}else{
				if (equalType(Token.TipoToken.PARABRE)){
					consumirToken();
					Tipo tipo = Expression(metodo,ladoIzq);
					consumirTokenCondicional(Token.TipoToken.PARCIERRA);
					//<Primary>.tipo = <Expression>.tipo;
					//<Primary>.asignable = false;
					return new ExpressionSintetizados(ladoIzq,false,tipo,"");
				}else{
					if (equalType(Token.TipoToken.NEW)){
						consumirToken();
						String identifier = token.getLexema();
						consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
					 
						//CHEQUEO SEMANTICO BEGIN
						TClase ctor;
						if (tsG.existeClase(identifier)){ 
							ctor = tsG.getClase(identifier);
							if (!(ctor.existeMetodo(identifier))){
								throw new NewConstructorException(token.getLinea(),identifier);
							}
						  //*******************************************************************************************
							//GENERACIÓN CI
							cGen.generate("RMEM 1");
							//(el espacio para la regencia a la VT)
							// HAGO UN JUMP AL FINAL PARA LA INSTRUCCION PUSH PORQUE NO TENGO EL TAMAÑO DE TODOS LOS CIR CALCULADOS AUN
							String labelPrimary = lGen.generateLabel();
							String labelPrimaryRet = lGen.generateLabel();
							cGen.generate("JUMP "+labelPrimary); 
							cGen.generate(labelPrimaryRet+": NOP");
							InstructionClassGenerate primaryInstruction = new InstructionClassGenerate(ctor,labelPrimary,labelPrimaryRet,"PUSH");
							listClassInstruction.add(primaryInstruction);
							cGen.generate("PUSH malloc					;La dirección de la rutina para alojar memoria en el heap");
							cGen.generate("CALL					;Llamamos a malloc");
							cGen.generate("DUP					;Para no perder la referencia al nuevo CIR cuando hagamos el STOREREF para asociarle la VT");
							String vtable="VT_"+identifier;
							cGen.generate("PUSH "+vtable+"					;Apilamos la dirección del comienzo de la VT de "+identifier);
							cGen.generate("STOREREF 0					;se guarda la referencia a la VT en el CIR");
							cGen.generate("DUP					;duplicamos la referencia al objeto por que al finalizar la ejecución del constructor tiene que quedar en el tope de la pila");	
						  //*******************************************************************************************
							ActualArgs(ctor.getMetodo(identifier),ladoIzq);//<ActualArgs>.ctor = ctor
						  //*******************************************************************************************
							//GENERACIÓN CI		
							cGen.generate("PUSH l"+identifier+identifier+"					;no usamos la VT para apilar la dirección del constructor, ya que se puede determinar en tiempo de compilación");
							cGen.generate("CALL");
						  //*******************************************************************************************
						}else throw new NewConstructorException(token.getLinea(),identifier);
						//CHEQUEO SEMANTICO END
						//<Primary>.tipo = new TClase(identifier.getLexema);
						//<Primary>.asignable = false;
						Tipo tipo = tsG.getClase(identifier);
						return new ExpressionSintetizados(ladoIzq,false,tipo,"");
					}else{
						if (equalType(Token.TipoToken.SUPER)){
							consumirTokenCondicional(Token.TipoToken.SUPER);
							consumirTokenCondicional(Token.TipoToken.PUNTO);
							String identifier = token.getLexema();
							consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
							// CHEQUEO SEMANTICO BEGIN
							TClase padreActual = claseActual.getPadre();
							Metodo metSuper;
							if (!(padreActual.getNombre().equals(identifier))){
								if (claseActual.existeMetodoEnPadre(identifier)){
									metSuper = padreActual.getMetodo(identifier); 
								}else{
									throw new UndefinedMethodSuperException(token.getLinea(),identifier);
								}
							}else{
								throw new ConstructorSuperException(token.getLinea(),identifier);
							}
							//CHEQUEO SEMANTICO END
						  //*******************************************************************************************
							//GENERACIÓN CI
							  cGen.generate("LOAD 3");
							  if (!(metSuper.getTipo().tipoToString().equals("void"))){
			                    cGen.generate("RMEM 1");
			                    cGen.generate("SWAP");
							   }
						  //*******************************************************************************************
							ActualArgs(metSuper,ladoIzq);
							//<Primary>.asignable = false;
						   //*******************************************************************************************
							//GENERACIÓN CI
							cGen.generate("PUSH "+"l"+metSuper.getNombre()+padreActual.getNombre());
							cGen.generate("CALL");
						  //*******************************************************************************************
							return new ExpressionSintetizados(ladoIzq,false,metSuper.getTipo(),"");
						}else{
							if (Literal()){
								Tipo tipo = getTipoLiteral();
								consumirToken();
								//System.out.println(token.getLexema());
								//<Primary>.tipo = <Literal>.tipo;
								//<Primary>.asignable = false;
								return new ExpressionSintetizados(ladoIzq,false,tipo,"");
							}else{
								String identifier = token.getLexema();
								consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
								//CHEQUEO SEMANTICO BEGIN
								TClase claseAux;
								if (clase!= null){ //heredo la clase de Primary
									claseAux = (TClase)clase;
								}else{ // es un metodo de la clase actual
									claseAux = claseActual;
								//*******************************************************************************************
									//GENERACIÓN CI	
									if ((claseActual.existeMetodo(identifier))){
										cGen.generate("LOAD 3 					;PORQUE ESTOY EN LA CLASE ACTUAL");
									}
								//*******************************************************************************************
								}
								if (claseAux.existeMetodo(identifier)){
									if ((ladoIzq==true) && (!(claseAux.getMetodo(identifier).getTipo().tipoToString().equals("void")))){
										throw new MethodAssignableException(token.getLinea(),identifier,claseAux.getMetodo(identifier).getTipo().tipoToString());
									}
								}
								Metodo metActArgs;
								if (((claseAux.existeMetodo(identifier)) || (claseAux.existeMetodoEnPadre(identifier))) && (claseAux.esConstructor(identifier)==false)){
									metActArgs = claseAux.getMetodo(identifier);
								  //*******************************************************************************************
									//GENERACIÓN CI	
									if (!(metActArgs.getTipo().tipoToString().equals("void"))){ // SI TIENE VALOR DE RETORNO
										cGen.generate("RMEM 1");
										cGen.generate("SWAP");
									}
								  //*******************************************************************************************
								}else{ 
									metActArgs = null;
								  //*******************************************************************************************
									//GENERACIÓN CI	
										//ES ATRIBUTO (parametro del metodo)
										Atributo atr = metActual.obtenerAtributo(identifier);
										if (atr!=null){
											//es un parámetro, para cargar su valor debemos usar LOAD
											cGen.generate("LOAD "+atr.getOffset()+"					;offset del atr en el RA del parametro "+atr.getNombre());	 
										}else{
											// ES VARIABLE DE INSTANCIA 
											if (claseAux.existeVariable(identifier)){
												Variable var = claseAux.getVariable(identifier); 
												cGen.generate("LOAD 3					;Apilamos this por variable de instancia "+identifier); //Apilamos this
												// HAGO UN JUMP AL FINAL PARA LA INSTRUCCION LOADREF PORQUE NO TENGO EL OFFSET DE LA VARIABLE CALCULADO AUN
												String labelPrimaryId = lGen.generateLabel();
												String labelPrimaryIdRet = lGen.generateLabel();
												cGen.generate("JUMP "+labelPrimaryId); 
												cGen.generate(labelPrimaryIdRet+": NOP");
												InstructionGenerate newInstruction = new InstructionGenerate(var,labelPrimaryId,labelPrimaryIdRet,"LOADREF");
												listInstruction.add(newInstruction);	
											}
										}
										
										if (ladoIzq==true){
											if (equalType(Token.TipoToken.ASIG)){
												cGen.generate("POP");
											}
										}
										//Como en Decaf tenemos asignaciones múltiples debemos duplicar el valor evaluado para que quede en la pila
										//cGen.generate("DUP");
									//}
										//obs: SI NO ES NINGUNA DE LAS TRES TIRA ERROR SEMANTICO
								  //*******************************************************************************************
								}
								//CHEQUEO SEMANTICO END
								boolean asignableActArgs = PrimaryActualArgs(metActArgs,ladoIzq);
								//CHEQUEO SEMANTICO BEGIN
								Tipo tipoHeredado;
						
								if (metActArgs != null){
									tipoHeredado = metActArgs.getTipo(); // tipoHeredado es un tipo hererado, es el tipo devuelto del metodo	
								}else{	
									// Si clase == null es el primero y no heredo nada entonces puede ser variable
									if (clase==null){ 		
										Atributo atr = metActual.obtenerAtributo(identifier);
										if (atr!=null){
											 tipoHeredado = atr.getTipo();	
										}else{
											if (claseAux.existeVariable(identifier)){
												tipoHeredado = claseAux.getVariable(identifier).getTipo();
											}else{
												throw new UndeclaredIndentifierException(token.getLinea(),identifier);
											}
										}
									}else{
										throw new InvalidVariableException(token.getLinea(),identifier);
									}
								}
								//CHEQUEO SEMANTICO END
								PrimaryIdSintetizados primaryId = PrimaryIdentifier(metodo, ladoIzq, tipoHeredado);
								// CHEQUEO SEMANTICO BEGIN
								Tipo tipo;
								if (primaryId.getMasMetodos()==true){
									tipo = primaryId.getTipo();
								}else {
									if (metActArgs != null){
										tipo = metActArgs.getTipo();
									}else{ 
										Atributo atr = metActual.obtenerAtributo(identifier);
										if (atr!=null){
											tipo = atr.getTipo();		
										}else{
											if (claseAux.existeVariable(identifier)){
												Variable v = claseAux.getVariable(identifier);
												tipo = v.getTipo();												
											}else {
												throw new UndeclaredVariableException(token.getLinea(),identifier);
											}					
										}
									}
								}
								boolean asignable = (asignableActArgs && primaryId.getAsignable());
								String id;
								if (asignable == true){
									id = identifier;
								}else{
									id = "";
								}
								// CHEQUEO SEMANTICO END
								
								ExpressionSintetizados expResult =  new ExpressionSintetizados(ladoIzq,asignable,tipo,id);
								return (expResult);
							}
						}
					}
				}
			}
	}

	private PrimaryIdSintetizados PrimaryIdentifier(Metodo metodo, boolean ladoIzq, Tipo tipoHeredado) throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.PUNTO)){
			if (tipoHeredado.esPrimitivo()==true){
				throw new PrimityTypeException(token.getLinea());
			}else{
				consumirTokenCondicional(Token.TipoToken.PUNTO);
				ExpressionSintetizados primary = Primary(metodo,ladoIzq,tipoHeredado);
				PrimaryIdSintetizados primaryId = PrimaryIdentifier(metodo, ladoIzq, primary.getTipoExpression());
				//<PrimaryIdentifier>.tipo = <PrimaryIdentfier1>.tipoHeredado;	
				//<PrimaryIdentifier>.asignable = false;
				return new PrimaryIdSintetizados(primaryId.getTipo(),true,false);
			}
		}else{
			return new PrimaryIdSintetizados(tipoHeredado,false,true);
		}
	}

	private boolean PrimaryActualArgs(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.PARABRE)){
			if (metodo!=null){
				ActualArgs(metodo,ladoIzq); // hace los chequeos de los argumentos si es un metodo (si es null no hace nada)
			  //*******************************************************************************************
				//GENERACIÓN CI	
				cGen.generate("DUP");
				cGen.generate("LOADREF 0					;Offset VT de "+claseActual.getNombre()); 
				cGen.generate("LOADREF "+metodo.getOffset()+"					;Offset metodo "+metodo.getNombre()); // Offset metodo
				cGen.generate("CALL");
			  //*******************************************************************************************
				return false;
			}else{
				throw new UndeclaredMethodCallException(token.getLinea());
			}
		}else{
			return true;
		}
	}

	private void ActualArgs(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		consumirTokenCondicional(Token.TipoToken.PARABRE);
		NTExprList(metodo,ladoIzq);   //// heredo el metodo para poder chequear los tipos del argumento formal del mismo con los tipos de los argumentos actuales
		consumirTokenCondicional(Token.TipoToken.PARCIERRA);
	}
	
	private void NTExprList(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if (!(equalType(Token.TipoToken.PARCIERRA))){
			posArg = 0;
			ExprList(metodo,ladoIzq);
		}else{
			if (metodo.getCantParametros()!=0){
				throw new MinusActualAtributeException(token.getLinea(),metodo.getNombre());
			}
		}
	}

	private void ExprList(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		Tipo tipoExp = Expression(metodo,ladoIzq);
		//CHEQUEO SEMANTICO BEGIN
		if (posArg<metodo.getCantParametros()){
			if (metodo != null){ // si trae el obj Metodo hago el chequeo de los param
				if (tipoExp.conforma(metodo.getAtributo(posArg).getTipo())){
					posArg++;
					//<ExprListPrima>.metodo = <ExprList>.metodo;
				}else{
					throw new ActualArgsNotConformException(token.getLinea(),tipoExp.tipoToString());
				}
			} // si es null no hago nada porque no es un metodo
		}else{
			throw new MoreActualAtributeException(token.getLinea(),metodo.getNombre());
		}
		//CHEQUEO SEMANTICO END	
	  //*******************************************************************************************
		//GENERACIÓN CI
		//Luego de generar el código para la expresión del parámetro actual se hace SWAP para ir bajando el futuro this
		cGen.generate("SWAP");
	  //*******************************************************************************************
		ExprListPrima(metodo,ladoIzq);
	}

	private void ExprListPrima(Metodo metodo,boolean ladoIzq) throws LexicException, SintacticException,SemanticException{
		if (equalType(Token.TipoToken.COMA)){
			consumirTokenCondicional(Token.TipoToken.COMA);
			ExprList(metodo,ladoIzq);
		}else{
			if (metodo.getCantParametros()!=posArg){
				throw new MinusActualAtributeException(token.getLinea(),metodo.getNombre());
			}
			posArg = 0;
		}
	}

	private boolean Literal() throws LexicException, SintacticException,SemanticException{
	   //*******************************************************************************************
		//GENERACIÓN CI
		if (equalType(Token.TipoToken.TRUE)){
			cGen.generate("PUSH 1");
		}else{
			if (equalType(Token.TipoToken.FALSE)){
				cGen.generate("PUSH 0");
			}else{
				if (equalType(Token.TipoToken.CARACTER)){
					cGen.generate("PUSH '"+token.getLexema()+"'");
				}else{
					if (equalType(Token.TipoToken.NUMERO)){
						cGen.generate("PUSH "+token.getLexema());
					}else{
						if (equalType(Token.TipoToken.CADENA)){
							String valor = token.getLexema();
							int longitud=valor.length()+1;//agrego el lugar de la VTable (tam string+1)
							//cGen.generate(".CODE");
							//Cuando tienen que cargar un String llaman malloc con el tamaño del String+1
							cGen.generate("RMEM 1					;Celda para la referencia al CIR del string");
							cGen.generate("PUSH "+longitud+"					;Longitud del CIR para el string "+valor);
							cGen.generate("PUSH malloc					;Aloja el CIR para el string (crea un String)");
							cGen.generate("CALL"); 
							//Por cada letra del String que se genera
							for(int i=0; i<valor.length(); i++){
								char caracter=valor.charAt(i);
								cGen.generate("DUP ");
								cGen.generate("PUSH '"+caracter+"'");
								cGen.generate("STOREREF "+i+"					;posicion letra en el string"); 
							}
							//terminador de String
							cGen.generate("DUP");
							cGen.generate("PUSH 0");
							cGen.generate("STOREREF "+valor.length()+"					; A˜nade el terminador para el String");
						}
					}
				}
			}
		}
	  //*******************************************************************************************
		return ((equalType(Token.TipoToken.NULL)) || (equalType(Token.TipoToken.TRUE)) || (equalType(Token.TipoToken.FALSE)) || (equalType(Token.TipoToken.CADENA)) || (equalType(Token.TipoToken.CARACTER)) || (equalType(Token.TipoToken.NUMERO)));
	}
	
	private Tipo getTipoLiteral() throws LexicException, SintacticException,SemanticException{
		if ((equalType(Token.TipoToken.TRUE)) || (equalType(Token.TipoToken.FALSE))){
			return new Booleano();
		}else{
			if ((equalType(Token.TipoToken.CADENA))){
				return new Cadena();
			}else{
				if (equalType(Token.TipoToken.CARACTER)){
					return new Caracter();
				}else{
					if (equalType(Token.TipoToken.NUMERO)){
						return new Entero();
					}else{
						return new TVoid();
					}
				}
			}
		}
	}

	private ExpressionSintetizados PrimaryThis(Metodo metodo, boolean ladoIzq) throws LexicException, SintacticException, SemanticException{
		Tipo tipo = null;
		boolean asignable = false;
		String id = "";
		if (equalType(Token.TipoToken.PUNTO)){
			consumirTokenCondicional(Token.TipoToken.PUNTO);
			String identifier = token.getLexema();
			consumirTokenCondicional(Token.TipoToken.IDENTIFICADOR);
			//CHEQUEO SEMANTICO BEGIN
			if (claseActual.existeMetodo(identifier)){
				tipo = claseActual.getMetodo(identifier).getTipo();
				asignable = false; // tengo this.met() --> no es asignable
			   //*******************************************************************************************
				//GENERACIÓN CI
				if (!((ladoIzq==true) && (equalType(Token.TipoToken.ASIG)))){
					Metodo met = claseActual.getMetodo(identifier);			//getMetodo(identifier.getLexema());
					cGen.generate("LOAD 3					;POR EL THIS DE MET "+met.getNombre()); 
					// si devuelve algo
					if (!(met.getTipo().tipoToString().equals("void"))){					//if (met.getTipo()!= new Void())
						cGen.generate("RMEM 1");
						cGen.generate("SWAP");
					}	
				}
			   //*******************************************************************************************
				ActualArgs(claseActual.getMetodo(identifier),ladoIzq);
			}else{
				if (claseActual.existeVariable(identifier)){
					tipo = claseActual.getVariable(identifier).getTipo();  //claseActual.existeVariable(identifier.getLexema().getTipo();
					asignable = true; // tengo this.id --> es asignable
					id = identifier;
				}else{
					throw new UndeclaredIndentifierException(token.getLinea(),identifier);
				}
			}
			//CHEQUEO SEMANTICO END
		  //*******************************************************************************************
			//GENERACIÓN CI
			if ((ladoIzq==true) && (equalType(Token.TipoToken.ASIG))){
				//ES ATRIBUTO (parametro del metodo)
				Atributo atr = metodo.obtenerAtributo(identifier);
				if (atr!=null){
					cGen.generate("DUP");
					cGen.generate("STORE "+atr.getOffset()); //offset param en RA
				}else{
					// ES VARIABLE DE INSTANCIA 
					if (claseActual.existeVariable(identifier)){
						Variable var = claseActual.getVariable(identifier);			//getVariable(identifier.getLexema())
						cGen.generate("DUP");
						cGen.generate("LOAD 3					;this por la variable "+var.getNombre()); 
						cGen.generate("SWAP");						
						// HAGO UN JUMP AL FINAL PARA LA INSTRUCCION LOADREF PORQUE NO TENGO EL OFFSET DE LA VARIABLE CALCULADO AUN
						String labelPrimaryThis = lGen.generateLabel();
						String labelPrimaryThisRet = lGen.generateLabel();
						cGen.generate("JUMP "+labelPrimaryThis); 
						cGen.generate(labelPrimaryThisRet+": NOP");
						InstructionGenerate newInstruction = new InstructionGenerate(var,labelPrimaryThis,labelPrimaryThisRet,"STOREREF");
						listInstruction.add(newInstruction);
					}
				}
			}else{
				if (claseActual.existeMetodo(identifier)){					//existeMetodo(identifier.getLexema())
					Metodo met = claseActual.getMetodo(identifier);			//getMetodo(identifier.getLexema());
					cGen.generate("DUP");
					cGen.generate("LOADREF 0					;Offset VT de "+claseActual.getNombre());
					cGen.generate("LOADREF "+met.getOffset()+"					;Offset metodo "+met.getNombre());
					cGen.generate("CALL");
				}else{
					if (claseActual.existeVariable(identifier)){							//claseActual.existeVariable(identifier.getLexema())
						Variable var = claseActual.getVariable(identifier);					//getVariable(identifier.getLexema())
						cGen.generate("LOAD 3					;Apilamos this");
						// HAGO UN JUMP AL FINAL PARA LA INSTRUCCION LOADREF PORQUE NO TENGO EL OFFSET DE LA VARIABLE CALCULADO AUN
						String labelPrimaryThis = lGen.generateLabel();
						String labelPrimaryThisRet = lGen.generateLabel();
						cGen.generate("JUMP "+labelPrimaryThis); 
						cGen.generate(labelPrimaryThisRet+": NOP");
						InstructionGenerate newInstruction = new InstructionGenerate(var,labelPrimaryThis,labelPrimaryThisRet,"LOADREF");
						listInstruction.add(newInstruction);  
					}
				}
			}
	//*******************************************************************************************
		}
		return (new ExpressionSintetizados(ladoIzq,asignable,tipo,id));
	}
}
    





 
    

